Android怎么實現小米相機底部滑動指示器
發布時間:2021-04-15 14:39:38
來源:億速云
閱讀:94
作者:小新
這篇文章給大家分享的是有關Android怎么實現小米相機底部滑動指示器的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
先上一張圖看下效果:
主要實現功能有:
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怎么實現小米相機底部滑動指示器”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!