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.mzph.cn/news/536552.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mariadb修改root密码

2019独角兽企业重金招聘Python工程师标准>>> 默认情况下,新安装的 mariadb 的密码为空,在shell终端直接输入 mysql 就能登陆数据库。 如果是刚安装第一次使用,请使用 mysql_secure_installation 命令初始化。 # mysql_secure_inst…

自学html和css,学习HTML和CSS的5大理由

描述人们学习HTML和CSS最常见的原因是开始从事web开发。但并不是只有web开发人员才要学习HTML和CSS的核心技术。作为一个网络用户,你需要你掌握的相关技术很多,但下面有5个你无法拒绝学习HTML和CSS的理由。1、轻松制作卡通动画Web上的动画很多年来都是使…

html 左侧 树形菜单,vue左侧菜单,树形图递归实现代码

学习vue有一段时间了,最近使用vue做了一套后台管理系统,左侧菜单需求是这样的,可以多层,数据由后台传递。也因为自己对官方文档的不熟悉使得自己踩了不少坑,今天写出来和大家一起分享。效果图如下所示:先说…

关于Istio 1.1,你所不知道的细节

本文整理自Istio社区成员Star在 Cloud Native Days China 2019 北京站的现场分享 第1则 主角 Istio Istio作为service mesh领域的明星项目,从2016年发布到现在热度不断攀升。 Istio & Envoy Github Star Growth 官网中Istio1.1的架构图除了数据面的Envoy和控制面…

2021吉林高考26日几点可以查询成绩,2021吉林高考成绩查分时间及入口

2021吉林高考成绩查分时间及入口2021吉林高考成绩查分时间及入口,有一些高考生真的很积极,考完试当天就将答案给对好了,考试嘛,站在旁观者的角度来看总是有人欢喜有人忧。估出来分数不咋地的,整个六月就毁了。2021吉林…

easyui,layui和 vuejs 有什么区别

2019独角兽企业重金招聘Python工程师标准>>> easyui是功能强大但是有很多的组件使用功能是十分强大的,而layui是2016年才出来的前端框架,现在才更新到2.x版本还有很多的功能没有完善,也还存在一些不稳定的情况,但是lay…

广东2021高考成绩位次查询,广东一分一段表查询2021-广东省2021年一分一段统计表...

广东省高考一分一段表是同学们在填报高考志愿时的重要参考资料之一。根据一分一段表,大家不仅可以清楚地了解自己的高考成绩在全省的排名,还可以结合心仪的大学近3年在广东省的录取位次变化,判断出自己被录取的概率大概是多少。根据考试院公布…

PAKDD 2019 都有哪些重要看点?看这篇文章就够了!...

雷锋网 AI 科技评论按:亚太地区知识发现与数据挖掘国际会议(Pacific Asia Knowledge Discovery and Data Mining,PAKDD)是亚太地区数据挖掘领域的顶级国际会议,旨在为数据挖掘相关领域的研究者和从业者提供一个可自由 …

大学计算机基础书本里的毕业论文源稿,计算机基础毕业论文范文

计算机基础毕业论文范文导语:关于大学计算机基础的教学,需要不断探索与实践,实现更好的教学。下面是小编带来的计算机基础毕业论文,欢迎阅读与参考。论文:大学计算机基础教学的探索与实践摘要:大学计算机基…

计算机技术基础 VB 答案,《计算机技术基础(VB)》武汉理工大学20春作业一

计算机技术基础(VB)_作业一1.[判断题] 写在一行上的多条语句,应以逗号作为分隔符。奥鹏作业答案可以联系QQ 761296021A.正确B.错误正确答案:——B——2.[判断题] 滚动条的最小值、最大值、最小变动值、最大变动值属性均可自行设计。A.正确B.错误正确答案:——A——3…

Spring MVC 实现Excel的导入导出功能(2:Excel的导入优化和Excel的导出)

Excel的导入V2优化版 有些时候文件上传这一步骤由前端来处理,只将上传后的 URL 传输给后端(可以参考上一文中的图片上传功能),也就是导入请求中并不会直接处理 MultipartFile 对象,而是通过文件 URL 将其下载&#xff…

华为智能手环智能手表软件测试,一块智能手表的测试之旅,揭秘华为运动健康科学实验室...

随着消费者对健康生活的日益关注,随之而来的是智能可穿戴设备的蓬勃发展。一个手环,一个智能手表,都可以为消费者提供诸如心率,步数相关的数据统计。而更进阶的设备,则能为用户提供系统的运动解决方案以及监控人体健康…

软件测试都有哪些证书,软件测试都有哪些证书呀?有用吗?

OYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学…

超低频测试信号产生电路软件流程图,一种0_20Hz超低频信号发生器的设计与实现...

。。第22卷第4期增刊           仪 器 仪 表 学 报             2001年8月一种0~20Hz超低频信号发生器的设计与实现马彦恒 郭 利 于久恩 (军械工程学院 石家庄 050003)摘要 本文介绍了一种采用了主—从式双CPU结构,频率和幅度都…

datastage 使用小结

转载于:https://www.cnblogs.com/Guhan-xue/p/10758663.html

Teams 的逻辑架构与语音解决方案 - Official Posters Download

意外收获了前几天(0420)刚刚更新出来的Teams架构海报,分享给大家下载 Microsoft Teams IT architecture and telephony solutions postershttps://docs.microsoft.com/en-us/microsoftteams/teams-architecture-solutions-posters 接下来跟大…

ifix怎么装服务器系统上,ifix服务器和客户端配置

ifix服务器和客户端配置 内容精选换一换准备好服务端和客户端,根据组网规划完成物理服务器的物理组网。本文档中以3台客户端和3台TaiShan服务器作为服务端为例。本次部署流程中以3台客户端节点和3台TaiShan 200 服务器(型号2280)作为存储节点,网络包含前…

接口测试工具Postman(转)

接口测试工具Postman Postman是一款功能强大的HTTP调试与模拟插件,不仅可以调试简单的CSS、HTML、脚本等网页的基本信息,它还可以发送几乎所有类型的HTTP请求。Postman适用于不同的操作系统,Mac、WindowsX32、Windows X64、Linux系统等。本篇…

洛谷 P1372 又是毕业季I

可能所有的数论题都是这样玄学.... 题目链接:https://www.luogu.org/problemnew/show/P1372 这道题通过暴力的枚举可以发现是不可做的(当然我也不会做) 然后就有了这样一个思路: 这道题就是求:从1~n中取k个数&#xff…

在线图表编辑工具 draw.io 10.6.5 版本发布

draw.io 10.6.5 版本已发布,draw.io 是一款在线图表编辑工具, 可以用来编辑工作流、BPM、org charts、UML、ER图、网络拓朴图等。 新版本更新内容如下: 修复 VSDX 导入的类型变体逻辑 (style variation logic) 同一天发布的 10.6.…