2019独角兽企业重金招聘Python工程师标准>>>

近在系统学习自定义View这一块的知识,前面几篇基本都是理论知识,这篇博客着重从实战来加强对自定义View的理解与运用。实现的两种效果,分别代表自定义View与自定义ViewGroup。

效果图:

上面的是一个可以滑动的刻度尺,支持快速滑动,选择的数字也会显示在下方;下面的是一个经典的流式布局,会根据文字长度自动进行布局。一起看看怎么实现的吧:

一.准备工作

1.布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:myscroll="http://schemas.android.com/apk/res-auto"android:id="@+id/activity_five"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/white"android:orientation="vertical"><org.tyk.android.artstudy.MySelectViewandroid:id="@+id/my_selectview"android:layout_width="match_parent"android:layout_height="50dp"android:layout_marginTop="50dp"myscroll:lineColor="@color/font_text"myscroll:textColor="@color/strong"myscroll:textSize="20dp"></org.tyk.android.artstudy.MySelectView><TextViewandroid:id="@+id/number_txt"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="5dp"android:layout_marginTop="20dp"android:text="选择的数字为:"android:textSize="20dp" /><org.tyk.android.artstudy.MyFlowLayoutandroid:id="@+id/my_flowlayout"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginTop="50dp"android:background="@color/bg_page"></org.tyk.android.artstudy.MyFlowLayout></LinearLayout>

从上到下的线性布局,依次是滑动刻度尺,数字TextView,流式布局,以及设置了一些自定义的属性。

2.自定义滑动刻度尺的初始准备

    public MySelectView(Context context) {this(context, null);}public MySelectView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public MySelectView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);//获取我们自定义的样式属性TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MySelectView, defStyleAttr, 0);int n = array.getIndexCount();for (int i = 0; i < n; i++) {int attr = array.getIndex(i);switch (attr) {case R.styleable.MySelectView_lineColor:// 默认颜色设置为黑色lineColor = array.getColor(attr, Color.BLACK);break;case R.styleable.MySelectView_textColor:textColor = array.getColor(attr, Color.BLACK);break;case R.styleable.MySelectView_textSize:// 默认设置为16sp,TypeValue也可以把sp转化为pxtextSize = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));break;}}array.recycle();init();}public void init() {mPaint = new Paint();mPaint.setAntiAlias(true);bigBound = new Rect();smallBound = new Rect();}public void setmStartWidth(int mStartWidth) {this.mStartWidth = mStartWidth;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int width;int height;int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);if (widthMode == MeasureSpec.EXACTLY) {width = widthSize;} else {width = widthSize * 1 / 2;}if (heightMode == MeasureSpec.EXACTLY) {height = heightSize;} else {height = heightSize * 1 / 2;}setMeasuredDimension(width, height);}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);mWidth = getWidth();mHeight = getHeight();mStartWidth = 0;}

初始化自定义控件,获取自定义控件的样式属性,初始化相关工具,重写onMeasure()测量自定义控件大小,重写onLayout()获取自定义控件宽高。

二.自定义滑动刻度尺的实现

1.重写onDraw()方法绘制刻度尺

    @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);mPaint.setColor(lineColor);//画背景canvas.drawLine(0, 0, mWidth, 0, mPaint);canvas.drawLine(0, mHeight, mWidth, mHeight, mPaint);//画数字for (int i = 0; i < 1000; i++) {if (i % 5 == 0) {mPaint.setColor(textColor);canvas.drawLine(mStartWidth, 0, mStartWidth, getHeight() / 3, mPaint);mPaint.setTextSize(textSize);mPaint.getTextBounds(String.valueOf(i), 0, String.valueOf(i).length(), bigBound);canvas.drawText(String.valueOf(i), mStartWidth - bigBound.width() / 2, getHeight() / 2 + bigBound.height() * 3 / 4, mPaint);} else {mPaint.setColor(lineColor);mPaint.setTextSize(textSize - 15);canvas.drawLine(mStartWidth, 0, mStartWidth, getHeight() / 5, mPaint);mPaint.getTextBounds(String.valueOf(i), 0, String.valueOf(i).length(), smallBound);canvas.drawText(String.valueOf(i), mStartWidth - smallBound.width() / 2, getHeight() / 2 + smallBound.height() * 3 / 4, mPaint);}mStartWidth += mWidth / 10;}//画中间刻度线mPaint.setColor(textColor);canvas.drawLine(mWidth / 2, 0, mWidth / 2, getHeight() / 3, mPaint);}

绘制背景的两条实线,绘制中间的数字,绘制中间的刻度线。

2.重写onTouchEvent()方法处理滑动事件

    @Overridepublic boolean onTouchEvent(MotionEvent event) {if (velocityTracker == null) {velocityTracker = VelocityTracker.obtain();}velocityTracker.addMovement(event);int x = (int) event.getX();switch (event.getAction()) {case MotionEvent.ACTION_DOWN:xDown = x;break;case MotionEvent.ACTION_MOVE:xMove = x;mStartWidth = xScroll + (xMove - xDown);invalidate();int numberScroll = (int) Math.round(Double.valueOf(mStartWidth) / Double.valueOf(mWidth / 10));listener.getNumber(Math.abs(numberScroll - 5));break;case MotionEvent.ACTION_UP:xUp = x;xScroll = xScroll + (xUp - xDown);//处理快速滑动velocityTracker.computeCurrentVelocity(1000);int scrollX = (int) velocityTracker.getXVelocity();xScroll = xScroll + scrollX;ValueAnimator walkAnimator = ValueAnimator.ofInt(mStartWidth, xScroll);walkAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mStartWidth = (int) animation.getAnimatedValue();invalidate();}});walkAnimator.setDuration(500);walkAnimator.start();walkAnimator.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {//处理惯性滑动int endX = xScroll % (mWidth / 10);if (Math.abs(endX) < mWidth / 20) {xScroll = xScroll - endX;mStartWidth = xScroll;invalidate();} else {xScroll = xScroll + (Math.abs(endX) - mWidth / 10);mStartWidth = xScroll;invalidate();}}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}});int number = (int) Math.round(Double.valueOf(xScroll) / Double.valueOf(mWidth / 10));listener.getNumber(Math.abs(number - 5));break;}return true;}

这一块是整个自定义滑动刻度尺的重点,慢慢分析一下:

初始化VelocityTracker,并且把要追踪的MotionEvent注册到VelocityTracker的监听中,用来跟踪触摸屏事件,主要用来处理滑动刻度尺的快速滑动。

MotionEvent.ACTION_DOWN: 获取水平方向X的坐标

MotionEvent.ACTION_MOVE: 获取水平方向滑动的距离,然后不断改变绘制的开始位置,再调用invalidate()来进行重绘,达到滑动的效果。后面两句代码是为了让下面显示的数字能够实时更新,接口回调。

MotionEvent.ACTION_UP: 
1.获取滑动到总距离。 
2.处理快速滑动,首先获取1秒内X方向所滑动像素值,然后确定最终滑动的位置。通过一个属性动画,不断改变绘制的开始位置,再调用invalidate()来进行重绘,达到快速滑动的效果。 
3.处理惯性滑动,仔细查看效果图你会发现,当最后滑动的终点位置不足一半时,会自动滑动到前一个位置;当最后滑动的终点位置超过一半时,会自动滑动到下一个位置。这里其实就是在动画结束的时候,进行判断,然后调用invalidate()来进行重绘,达到惯性滑动的效果。 
4.最后两句代码是为了让下面显示的数字最终能够实时更新,接口回调。

三.自定义流式布局的实现

@Overridepublic ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {return new MarginLayoutParams(getContext(), attrs);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);// 计算出所有的childView的宽和高for (int i = 0; i < getChildCount(); i++) {View childView = getChildAt(i);measureChild(childView, widthMeasureSpec, heightMeasureSpec);}setMeasuredDimension(sizeWidth, sizeHeight);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {int left = 0;int top = 0;for (int i = 0; i < getChildCount(); i++) {View childView = getChildAt(i);MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams();int lc = left + params.leftMargin;int rc = childView.getMeasuredWidth() + lc;int tc = top + params.topMargin;int bc = childView.getMeasuredHeight() + tc;childView.layout(lc, tc, rc, bc);//超过宽度则换行if (rc + childView.getMeasuredWidth() > getMeasuredWidth()) {left = 0;top = bc;} else {left = rc;}}}

1.重写generateLayoutParams()方法返回MarginLayoutParams的实例,使自定义的流式布局能够支持margin属性 
2.重写onDraw()方法计算出所有的childView的宽和高以及测量模式,并且设置自己的宽高 
3.重写onLayout()方法对所有childView进行定位(设置childView的绘制区域),并且根据childView的宽度进行自动换行。

具体使用:

    public void init() {stringList.add("数据库");stringList.add("移动开发");stringList.add("前端开发");stringList.add("微信小程序");stringList.add("服务器开发");stringList.add("PHP");stringList.add("人工智能");stringList.add("大数据");mySelectView = (MySelectView) findViewById(R.id.my_selectview);myFlowLayout = (MyFlowLayout) findViewById(R.id.my_flowlayout);for (String textView : stringList) {LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);params.setMargins(40, 40, 40, 40);TextView showText = new TextView(this);showText.setLayoutParams(params);showText.setTextColor(getResources().getColor(R.color.text_color));showText.setTextSize(20);showText.setText(textView);showText.setBackground(getResources().getDrawable(R.drawable.flag_01));myFlowLayout.addView(showText);}numberTxt = (TextView) findViewById(R.id.number_txt);mySelectView.setListener(this);}

将需要设置的文字动态添加到我们的流式布局中去即可,自定义的流式布局会自动根据添加文字的大小进行布局,达到最后的效果。

源码地址:

https://github.com/18722527635/AndroidArtStudy

转载于:https://my.oschina.net/u/2933456/blog/873910

自定义View----滑动刻度尺与流式布局 实例(四)相关推荐

  1. Android自定义View之实现流式布局

    Android自定义View之实现流式布局 运行效果 流式布局 把子控件从左到右摆放,如果一行放不下,自动放到下一行 自定义布局流程 1. 自定义属性:声明,设置,解析获取自定义值 在attr.xml ...

  2. 扫二维码+阶梯流式布局+自定义画圆+组合view

    组合view package com.bawei.zhouyilianxi;import android.content.Context; import android.util.AttributeS ...

  3. android自定义view流布局,Android控件进阶-自定义流式布局和热门标签控件

    一.概述: 在日常的app使用中,我们会在android 的app中看见 热门标签等自动换行的流式布局,今天,我们就来看看如何 自定义一个类似热门标签那样的流式布局吧 类似的自定义换行流式布局控件.下 ...

  4. Android自定义组合布局,Android 流式布局 + 自定义组合控件

    自定义组合控件 package yanjupeng.bawei.com.day09.two; import android.content.Context; import android.util.A ...

  5. Android自定义ViewGroup的布局,往往都是从流式布局开始

    前言 前面几篇我们简单的复习了一下自定义 View 的测量与绘制,并且回顾了常见的一些事件的处理方式. 那么如果我们想自定义 ViewGroup 的话,它和自定义View又有什么区别呢?其实我们把 V ...

  6. 自定义 FlowLayout流式布局搜索框 加 GreenDao存取搜索记录,使用RecyclerView展示

    输入框布局的shape <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android ...

  7. android自定义流式布局思路,Android 自定义控件基础-流式布局

    什么是流式布局?其实我们在平时遇到过,只是有可能叫不出它的名字. 如图: 如上图,就是一个流式布局的样式. &esmp;这里,将记录一下怎么实现这个功能.其实实现这个功能的方法,就是自定义Vi ...

  8. Android 自定义UI 实战 02 流式布局

    Android 自定义UI 实战 02 流式布局-- 自定义ViewGroup 第二章 自定义ViewGroup 流式布局 文章目录 Android 自定义UI 实战 02 流式布局-- 自定义Vie ...

  9. Android自定义流式布局-FlowLayout

    很多App在搜索页时都有对热门标签的一个排列,而Android官方没有对应的布局控件,所以自己写了一个流式布局(也称标签布局)--FlowLayout. 为了大家使用的方便,没有添加自定义属性,所以需 ...

最新文章

  1. 【Python-ML】神经网络-多层感知器增加梯度检验
  2. 【Python-ML】SKlearn库密度聚类DBSCAN模型
  3. deepin--解决网易云音乐个性推荐无法加载
  4. linux 3gpp格式转换器,Movavi Video Converter
  5. 【Unity3D】Tags和Layers
  6. python和嵌入式哪个容易_嵌入式与python选哪个
  7. 基于netty的微服务网关_基于Rx-netty和Karyon2的云就绪微服务
  8. 【操作系统复习】进程控制
  9. 计算机主机制作过程,计算机主机箱的制作方法
  10. 关于HP C7K的firmware management中的power policy理解
  11. 使用高效代理抓取58同城巴州二手房信息并保存至excel
  12. J2me实现的wap浏览器
  13. [论文翻译]YOLOX: Exceeding YOLO Series in 2021
  14. 致远OA单点登陆二次开发的实现(含源码)
  15. 为什么爱因斯坦反对《自然辨证法》
  16. ubuntu进去安全模式_ubuntu 14.04全攻略
  17. Mathorcup数学建模竞赛第五届-【妈妈杯】A题:城市相邻两交叉口信号配时优化(附一等奖获奖论文和matlab代码实现)
  18. c语言录入信息后无法显示桌面,输入法在桌面不显示,怎么修改能显示在桌面任 – 手机爱问...
  19. 论文笔记(精读文章) - Invisible Backdoor Attack with Sample-Specific Triggers
  20. Proxmox VE(PVE)配置xterm.js终端

热门文章

  1. 如何在Ubuntu 18.04上安装Elasticsearch Logstash Kibana(Elastic Stack)
  2. Spring Boot –无法确定数据库类型NONE的嵌入式数据库驱动程序类
  3. shell学习之创建函数
  4. DotNetTextBox V3.0 所见即所得编辑器控件Ver3.2.5 Free(免费版)
  5. (二)可变分区存储管理方案中的内存分配
  6. 小米6.0以上系统怎么无需root激活Xposed框架的步骤
  7. 海鲜之战:上半场活鲜炒作已曲终,下半场冷冻海鲜厚积薄发?
  8. 个推 Spark实践教你绕过开发那些“坑”
  9. 快速排序—三路快排 vs 双基准
  10. 「陶哲軒實分析」 習題 3.5.9