android camera滑動,Android怎么實現小米相機底部滑動指示器

Android怎么實現小米相機底部滑動指示器

發布時間:2021-04-15 14:39:38

來源:億速云

閱讀:94

作者:小新

這篇文章給大家分享的是有關Android怎么實現小米相機底部滑動指示器的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

先上一張圖看下效果:

4dc4a612f1b401690c474c00e61682a2.png

主要實現功能有:

1.支持左右滑動,每次滑動一個tab

2.支持tab點擊,直接跳到對應tab

3.選中的tab一直處于居中位置

4.支持部分UI自定義(大家可根據需要自己改動)

5.tab點擊回調

6.內置Tab接口,放入的內容需要實現Tab接口

7.設置預選中tabpublic?class?CameraIndicator?extends?LinearLayout?{

//?當前選中的位置索引

private?int?currentIndex;

//tabs集合

private?Tab[]?tabs;

//?利用Scroller類實現最終的滑動效果

public?Scroller?mScroller;

//滑動執行時間(ms)

private?int?mDuration?=?300;

//選中text的顏色

private?int?selectedTextColor?=?0xffffffff;

//未選中的text的顏色

private?int?normalTextColor?=?0xffffffff;

//選中的text的背景

private?Drawable?selectedTextBackgroundDrawable;

private?int?selectedTextBackgroundColor;

private?int?selectedTextBackgroundResources;

//是否正在滑動

private?boolean?isScrolling?=?false;

private?int?onLayoutCount?=?0;

public?CameraIndicator(Context?context)?{

this(context,?null);

}

public?CameraIndicator(Context?context,?@Nullable?AttributeSet?attrs)?{

this(context,?attrs,?0);

}

public?CameraIndicator(Context?context,?@Nullable?AttributeSet?attrs,?int?defStyleAttr)?{

super(context,?attrs,?defStyleAttr);

mScroller?=?new?Scroller(context);

}

@Override

protected?void?onMeasure(int?widthMeasureSpec,?int?heightMeasureSpec)?{

super.onMeasure(widthMeasureSpec,?heightMeasureSpec);

int?widthMode?=?MeasureSpec.getMode(widthMeasureSpec);

int?widthSize?=?MeasureSpec.getSize(widthMeasureSpec);

int?heightMode?=?MeasureSpec.getMode(heightMeasureSpec);

int?heightSize?=?MeasureSpec.getSize(heightMeasureSpec);

//測量所有子元素

measureChildren(widthMeasureSpec,?heightMeasureSpec);

//處理wrap_content的情況

int?width?=?0;

int?height?=?0;

if?(getChildCount()?==?0)?{

setMeasuredDimension(0,?0);

}?else?if?(widthMode?==?MeasureSpec.AT_MOST?&&?heightMode?==?MeasureSpec.AT_MOST)?{

for?(int?i?=?0;?i?

View?child?=?getChildAt(i);

width?+=??child.getMeasuredWidth();

height?=?Math.max(height,?child.getMeasuredHeight());

}

setMeasuredDimension(width,?height);

}?else?if?(widthMode?==?MeasureSpec.AT_MOST)?{

for?(int?i?=?0;?i?

View?child?=?getChildAt(i);

width?+=??child.getMeasuredWidth();

}

setMeasuredDimension(width,?heightSize);

}?else?if?(heightMode?==?MeasureSpec.AT_MOST)?{

for?(int?i?=?0;?i?

View?child?=?getChildAt(i);

height?=?Math.max(height,?child.getMeasuredHeight());

}

setMeasuredDimension(widthSize,?height);

}?else?{

//如果自定義ViewGroup之初就已確認該ViewGroup寬高都是match_parent,那么直接設置即可

setMeasuredDimension(widthSize,?heightSize);

}

}

@Override

protected?void?onLayout(boolean?changed,?int?l,?int?t,?int?r,?int?b)?{

//給選中text的添加背景會多次進入onLayout,會導致位置有問題,暫未解決

if?(onLayoutCount?>?0)?{

return;

}

onLayoutCount++;

int?counts?=?getChildCount();

int?childLeft?=?0;

int?childRight?=?0;

int?childTop?=?0;

int?childBottom?=?0;

//居中顯示

int?widthOffset?=?0;

//計算最左邊的子view距離中心的距離

for?(int?i?=?0;?i?

View?childView?=?getChildAt(i);

widthOffset?+=?childView.getMeasuredWidth()?+?getMargins(childView).get(0)+getMargins(childView).get(2);

}

//計算出每個子view的位置

for?(int?i?=?0;?i?

View?childView?=?getChildAt(i);

childView.setOnClickListener(v?->?moveTo(v));

if?(i?!=?0)?{

View?preView?=?getChildAt(i?-?1);

childLeft?=?preView.getRight()?+getMargins(preView).get(2)+?getMargins(childView).get(0);

}?else?{

childLeft?=?(getWidth()?-?getChildAt(currentIndex).getMeasuredWidth())?/?2?-?widthOffset;

}

childRight?=?childLeft?+?childView.getMeasuredWidth();

childTop?=?(getHeight()?-?childView.getMeasuredHeight())?/?2;

childBottom?=?(getHeight()?+?childView.getMeasuredHeight())?/?2;

childView.layout(childLeft,?childTop,?childRight,?childBottom);

}

TextView?indexText?=?(TextView)?getChildAt(currentIndex);

changeSelectedUIState(indexText);

}

private?List?getMargins(View?view)?{

LayoutParams?params?=?(LayoutParams)?view.getLayoutParams();

List?listMargin?=?new?ArrayList();

listMargin.add(params.leftMargin);

listMargin.add(params.topMargin);

listMargin.add(params.rightMargin);

listMargin.add(params.bottomMargin);

return?listMargin;

}

@Override

public?void?computeScroll()?{

if?(mScroller.computeScrollOffset())?{

//?滑動未結束,內部使用scrollTo方法完成實際滑動

scrollTo(mScroller.getCurrX(),?mScroller.getCurrY());

invalidate();

}?else?{

//滑動完成

isScrolling?=?false;

if?(listener?!=?null)?{

listener.onChange(currentIndex,tabs[currentIndex]);

}

}

super.computeScroll();

}

/**

*?改變選中TextView的顏色

*

*?@param?currentIndex?滑動之前選中的那個

*?@param?nextIndex????滑動之后選中的那個

*/

public?final?void?scrollToNext(int?currentIndex,?int?nextIndex)?{

TextView?selectedText?=?(TextView)?getChildAt(currentIndex);

if?(selectedText?!=?null)?{

selectedText.setTextColor(normalTextColor);

selectedText.setBackground(null);

}

selectedText?=?(TextView)?getChildAt(nextIndex);

if?(selectedText?!=?null)?{

changeSelectedUIState(selectedText);

}

}

private?void?changeSelectedUIState(TextView?view)?{

view.setTextColor(selectedTextColor);

if?(selectedTextBackgroundDrawable?!=?null)?{

view.setBackground(selectedTextBackgroundDrawable);

}

if?(selectedTextBackgroundColor?!=?0)?{

view.setBackgroundColor(selectedTextBackgroundColor);

}

if?(selectedTextBackgroundResources?!=?0)?{

view.setBackgroundResource(selectedTextBackgroundResources);

}

}

/**

*?向右滑一個

*/

public?void?moveToRight()?{

moveTo(getChildAt(currentIndex?-?1));

}

/**

*?向左滑一個

*/

public?void?moveToLeft()?{

moveTo(getChildAt(currentIndex?+?1));

}

/**

*?滑到目標view

*

*?@param?view?目標view

*/

private?void?moveTo(View?view)?{

for?(int?i?=?0;?i?

if?(view?==?getChildAt(i))?{

if?(i?==?currentIndex)?{

//不移動

break;

}?else?if?(i?

//向右移

if?(isScrolling)?{

return;

}

isScrolling?=?true;

int?dx?=?getChildAt(currentIndex).getLeft()?-?view.getLeft()?+?(getChildAt(currentIndex).getMeasuredWidth()?-?view.getMeasuredWidth())?/?2;

//這里使用scroll會使滑動更平滑不卡頓,scroll會根據起點、終點及時間計算出每次滑動的距離,其內部有一個插值器

mScroller.startScroll(getScrollX(),?0,?-dx,?0,?mDuration);

scrollToNext(currentIndex,?i);

setCurrentIndex(i);

invalidate();

}?else?if?(i?>?currentIndex)?{

//向左移

if?(isScrolling)?{

return;

}

isScrolling?=?true;

int?dx?=?view.getLeft()?-?getChildAt(currentIndex).getLeft()?+?(view.getMeasuredWidth()?-?getChildAt(currentIndex).getMeasuredWidth())?/?2;

mScroller.startScroll(getScrollX(),?0,?dx,?0,?mDuration);

scrollToNext(currentIndex,?i);

setCurrentIndex(i);

invalidate();

}

}

}

}

/**

*?設置tabs

*

*?@param?tabs

*/

public?void?setTabs(Tab...?tabs)?{

this.tabs?=?tabs;

//暫時不通過layout布局添加textview

if?(getChildCount()>0){

removeAllViews();

}

for?(Tab?tab?:?tabs)?{

TextView?textView?=?new?TextView(getContext());

textView.setText(tab.getText());

textView.setTextSize(14);

textView.setTextColor(selectedTextColor);

textView.setPadding(dp2px(getContext(),5),?dp2px(getContext(),2),?dp2px(getContext(),5),dp2px(getContext(),2));

LayoutParams?layoutParams=?new?LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);

layoutParams.rightMargin=dp2px(getContext(),2.5f);

layoutParams.leftMargin=dp2px(getContext(),2.5f);

textView.setLayoutParams(layoutParams);

addView(textView);

}

}

public?int?getCurrentIndex()?{

return?currentIndex;

}

//設置默認選中第幾個

public?void?setCurrentIndex(int?currentIndex)?{

this.currentIndex?=?currentIndex;

}

//設置滑動時間

public?void?setDuration(int?mDuration)?{

this.mDuration?=?mDuration;

}

public?void?setSelectedTextColor(int?selectedTextColor)?{

this.selectedTextColor?=?selectedTextColor;

}

public?void?setNormalTextColor(int?normalTextColor)?{

this.normalTextColor?=?normalTextColor;

}

public?void?setSelectedTextBackgroundDrawable(Drawable?selectedTextBackgroundDrawable)?{

this.selectedTextBackgroundDrawable?=?selectedTextBackgroundDrawable;

}

public?void?setSelectedTextBackgroundColor(int?selectedTextBackgroundColor)?{

this.selectedTextBackgroundColor?=?selectedTextBackgroundColor;

}

public?void?setSelectedTextBackgroundResources(int?selectedTextBackgroundResources)?{

this.selectedTextBackgroundResources?=?selectedTextBackgroundResources;

}

public?interface?OnSelectedChangedListener?{

void?onChange(int?index,?Tab?tag);

}

private?OnSelectedChangedListener?listener;

public?void?setOnSelectedChangedListener(OnSelectedChangedListener?listener)?{

if?(listener?!=?null)?{

this.listener?=?listener;

}

}

private?int?dp2px(Context?context,?float?dpValue)?{

DisplayMetrics?metrics?=?context.getResources().getDisplayMetrics();

return?(int)?(metrics.density?*?dpValue?+?0.5F);

}

public?interface?Tab{

String?getText();

}

private?float?startX?=?0f;

@Override

public?boolean?onTouchEvent(MotionEvent?event)?{

if?(event.getAction()?==?MotionEvent.ACTION_DOWN)?{

startX?=?event.getX();

}

if?(event.getAction()?==?MotionEvent.ACTION_UP)?{

float?endX?=?event.getX();

//向左滑條件

if?(endX?-?startX?>?50?&&?currentIndex?>?0)?{

moveToRight();

}

if?(startX?-?endX?>?50?&&?currentIndex?

moveToLeft();

}

}

return?true;

}

@Override

public?boolean?onInterceptTouchEvent(MotionEvent?event)?{

if?(event.getAction()?==?MotionEvent.ACTION_DOWN)?{

startX?=?event.getX();

}

if?(event.getAction()?==?MotionEvent.ACTION_UP)?{

float?endX?=?event.getX();

//向左滑條件

if?(Math.abs(startX-endX)>50){

onTouchEvent(event);

}

}

return?super.onInterceptTouchEvent(event);

}

}

在Activity或fragment中使用private?var?tabs?=?listOf("慢動作",?"短視頻",?"錄像",?"拍照",?"108M",?"人像",?"夜景",?"萌拍",?"全景",?"專業")

lateinit?var??imageAnalysis:ImageAnalysis

override?fun?initView()?{

//實現了CameraIndicator.Tab的對象

val?map?=?tabs.map?{

CameraIndicator.Tab?{?it?}

}?.toTypedArray()??:?arrayOf()

//將tab集合設置給cameraIndicator,(binding.cameraIndicator即xml布局里的控件)

binding.cameraIndicator.setTabs(*map)

//默認選中??拍照

binding.cameraIndicator.currentIndex?=?3

//點擊某個tab的回調

binding.cameraIndicator.setSelectedTextBackgroundResources(R.drawable.selected_text_bg)

binding.cameraIndicator.setOnSelectedChangedListener?{?index,?tag?->

Toast.makeText(this,tag.text,Toast.LENGTH_SHORT).show()

}

}

感謝各位的閱讀!關于“Android怎么實現小米相機底部滑動指示器”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/536552.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536552.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536552.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

laravel安裝laravel-ide-helper擴展進行代碼提示(二)

一、擴展的地址 https://github.com/barryvdh/laravel-ide-helper二、安裝擴展 1、引入庫: composer require barryvdh/laravel-ide-helper composer require doctrine/dbal如果只想在開發環境上使用,請加上--dev composer require --dev barryvdh/larav…

android md 顏色,安卓MD(Material Design)規范

Md規范是一種設計風格,并不特指規范。是一種模擬紙張的手法。一、核心思想把物理世界的體驗帶進屏幕。去掉現實中的雜質和隨機性,保留其最原始純凈的形態、空間關系、變化與過度,配合虛擬世界的靈活特性,還原最貼近真實的體驗&…

Mariadb修改root密碼

2019獨角獸企業重金招聘Python工程師標準>>> 默認情況下,新安裝的 mariadb 的密碼為空,在shell終端直接輸入 mysql 就能登陸數據庫。 如果是剛安裝第一次使用,請使用 mysql_secure_installation 命令初始化。 # mysql_secure_inst…

【譯】Googler如何解決編程問題

本文是Google工程師Steve Merritt的一篇博客,向大家介紹他自己和身邊的同事解決編程問題的方法。 原文地址:blog.usejournal.com/how-a-googl… 在本文中,我將完整的向你介紹一種解決編程問題的策略,這個策略是我在日常工作中一直…

自學html和css,學習HTML和CSS的5大理由

描述人們學習HTML和CSS最常見的原因是開始從事web開發。但并不是只有web開發人員才要學習HTML和CSS的核心技術。作為一個網絡用戶,你需要你掌握的相關技術很多,但下面有5個你無法拒絕學習HTML和CSS的理由。1、輕松制作卡通動畫Web上的動畫很多年來都是使…

html 左側 樹形菜單,vue左側菜單,樹形圖遞歸實現代碼

學習vue有一段時間了,最近使用vue做了一套后臺管理系統,左側菜單需求是這樣的,可以多層,數據由后臺傳遞。也因為自己對官方文檔的不熟悉使得自己踩了不少坑,今天寫出來和大家一起分享。效果圖如下所示:先說…

Node.js的基本使用3

koa(擴展知識, 建議學習) koa是express超集(進階版)前后端分離和耦合概念介紹 面向過程 -》 面向對象 --》 面向服務數據庫 Node.js mongodb(bson json的超集) 分類: 關系型數據庫: MySql非關系型數據庫: MongoDB Mong…

Flutter的滾動以及sliver約束

Flutter框架中有很多滾動的Widget,ListView、GridView等,這些Widget都是使用Scrollable配合Viewport來完成滾動的。我們來分析一下這個滾動效果是怎樣實現的。 Scrollable在滾動中的作用 Scrollable繼承自StatefulWidget,我們看一下他的State的build方法…

頁面增加html,為靜態頁面HTML增加session功能

一般來說,只有服務器端的CGI程序(ASP、PHP、JSP)具有session會話功能,用來保存用戶在網站期間(會話)的活動數據信息,而對于數量眾多的靜態頁面(HTML)來說,只能使用客戶端的cookies來保存臨時活動數據,但對于cookies的操…

關于Istio 1.1,你所不知道的細節

本文整理自Istio社區成員Star在 Cloud Native Days China 2019 北京站的現場分享 第1則 主角 Istio Istio作為service mesh領域的明星項目,從2016年發布到現在熱度不斷攀升。 Istio & Envoy Github Star Growth 官網中Istio1.1的架構圖除了數據面的Envoy和控制面…

html調用父頁面的函數,js調用父框架函數與彈窗調用父頁面函數的方法

調用父級中的 aaa的函數子頁面中:οnclick"window.parent.frames.aaa()"父頁面中:function aaa(){alert(‘bbbbb’);}----------------------------------------------frame框架里的頁面要改其他同框架下的頁面或父框架的頁面就用parentwindow.opener引用的是window.…

讀卡距離和信號強度兩方面來考慮

選擇物聯宇手持終端機的時候,你可以參考以下幾個原則:選擇行業需要應用功能,能有效控制好預算。屏幕界面需要高清晰的,選用分辨率較高的能更好的支持展現。按照項目所需求的來分析,需要從讀卡距離和信號強度兩方面來考…

html script 放置位置,script標簽應該放在HTML哪里,總結分享

幾年前,有經驗的程序員總是讓我們將很明顯,現在瀏覽器有了更加酷的兼容方式,這篇文章,俺將跟大家一起來學習script標簽的async和defer新特性,探討script應該放在哪里更好。頁面加載方式在我們討論當瀏覽器加載帶有獲取…

2021吉林高考26日幾點可以查詢成績,2021吉林高考成績查分時間及入口

2021吉林高考成績查分時間及入口2021吉林高考成績查分時間及入口,有一些高考生真的很積極,考完試當天就將答案給對好了,考試嘛,站在旁觀者的角度來看總是有人歡喜有人憂。估出來分數不咋地的,整個六月就毀了。2021吉林…

easyui,layui和 vuejs 有什么區別

2019獨角獸企業重金招聘Python工程師標準>>> easyui是功能強大但是有很多的組件使用功能是十分強大的,而layui是2016年才出來的前端框架,現在才更新到2.x版本還有很多的功能沒有完善,也還存在一些不穩定的情況,但是lay…

廣東2021高考成績位次查詢,廣東一分一段表查詢2021-廣東省2021年一分一段統計表...

廣東省高考一分一段表是同學們在填報高考志愿時的重要參考資料之一。根據一分一段表,大家不僅可以清楚地了解自己的高考成績在全省的排名,還可以結合心儀的大學近3年在廣東省的錄取位次變化,判斷出自己被錄取的概率大概是多少。根據考試院公布…

bootstrap-select動態生成數據,設置默認選項(默認值)

bootstrap-select設置選中的屬性是selected"selected",只要找出哪一項要設置為默認選項,再設置其屬性selected"selected"即可,親測有效。代碼如下: var currentId $(_this).attr(data_id);//目標id&#xff…

無法顯示驗證碼去掉html,如何去除驗證碼-模版風格-易通免費企業網站系統 - Powered by CmsEasy...

去除前臺用戶登錄驗證碼1.修改lib/default/user_act.php 注釋掉或刪除55-58行修改為//if(!session::get(verify) || front::post(verify)<>session::get(verify)) {//front::flash(驗證碼錯誤&#xff01;);//return;//} 復制代碼2.修改template/default/user/login.html…

webpack4打包工具

什么是webpack webpack 是一個現代 JavaScript 應用程序的靜態模塊打包器(module bundler)。當 webpack 處理應用程序時&#xff0c;它會遞歸地構建一個依賴關系圖(dependency graph)&#xff0c;其中包含應用程序需要的每個模塊&#xff0c;然后將所有這些模塊打包成一個或多個…

通過計算機網絡進行的商務活動包括,電子商務練習題及答案

“電子商務”練習題一、填空題1&#xff0e;EDI系統構成三要素包括數據標準化、(EDI軟件及硬件)和(通信網絡)。2.B2C電子商務模式主要有&#xff1a;門戶網站、(電子零售商)、(內容提供商)、(交易經紀人)和社區服務商。3. 影響消費者網上購物的因素&#xff1a;商品特性、(商品…