我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android开发之自定义CheckBox

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android开发之自定义CheckBox

要实现的效果如下

考虑到关键是动画效果,所以直接继承

View
。不过
CheckBox
的超类
CompoundButton
实现了
Checkable
接口,这一点值得借鉴。

下面记录一下遇到的问题,并从源码的角度解决。

问题一: 支持 wrap_content

由于是直接继承自

View
wrap_content
需要进行特殊处理。

View measure流程的MeasureSpec:


 
 public static class MeasureSpec {
  private static final int MODE_SHIFT = 30;
  private static final int MODE_MASK = 0x3 << MODE_SHIFT;
  
  public static final int UNSPECIFIED = 0 << MODE_SHIFT;
  
  public static final int EXACTLY  = 1 << MODE_SHIFT;
  
  public static final int AT_MOST  = 2 << MODE_SHIFT;
  
  public static int getMode(int measureSpec) {
   return (measureSpec & MODE_MASK);
  }
  
  public static int getSize(int measureSpec) {
   return (measureSpec & ~MODE_MASK);
  }
 }

从文档说明知道android为了节约内存,设计了

MeasureSpec
,它由
mode
size
两部分构成,做这么多终究是为了从父容器向子view传达长宽的要求。

mode有三种模式:

      1、UNSPECIFIED:父容器不对子view的宽高有任何限制

      2、EXACTLY:父容器已经为子view指定了确切的宽高

      3、AT_MOST:父容器指定最大的宽高,子view不能超过

wrap_content属于AT_MOST模式。

来看一下大致的measure过程:

在View中首先调用measure(),最终调用onMeasure()


protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
    getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
 }

setMeasuredDimension
设置
view
的宽高。再来看看
getDefaultSize()


public static int getDefaultSize(int size, int measureSpec) {
  int result = size;
  int specMode = MeasureSpec.getMode(measureSpec);
  int specSize = MeasureSpec.getSize(measureSpec);
  switch (specMode) {
  case MeasureSpec.UNSPECIFIED:
   result = size;
   break;
  case MeasureSpec.AT_MOST:
  case MeasureSpec.EXACTLY:
   result = specSize;
   break;
  }
  return result;
 }

由于

wrap_content
属于模式
AT_MOST
,所以宽高为
specSize
,也就是父容器的
size
,这就和
match_parent
一样了。支持
wrap_content
总的思路是重写
onMeasure()
具体点来说,模仿
getDefaultSize()
重新获取宽高。


 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  int heightSize = MeasureSpec.getSize(heightMeasureSpec);
  int width = widthSize, height = heightSize;
  if (widthMode == MeasureSpec.AT_MOST) {
   width = dp2px(DEFAULT_SIZE);
  }
  if (heightMode == MeasureSpec.AT_MOST) {
   height = dp2px(DEFAULT_SIZE);
  }
  setMeasuredDimension(width, height);
 }

问题二:Path.addPath()和PathMeasure结合使用

举例子说明问题:


 mTickPath.addPath(entryPath);
 mTickPath.addPath(leftPath);
 mTickPath.addPath(rightPath);
 mTickMeasure = new PathMeasure(mTickPath, false);
 // mTickMeasure is a PathMeasure

尽管

mTickPath
现在是由三个
path
构成,但是
mTickMeasure
此时的
length
entryPath
长度是一样的,到这里我就很奇怪了。看一下
getLength()
的源码:


 
 public float getLength() {
  return native_getLength(native_instance);
 }

从注释来看,获取的是当前

contour
的总长。

getLength
调用了
native
层的方法,到这里不得不看底层的实现了。

通过阅读源代码发现,

Path
PathMeasure
实际分别对应底层的
SKPath
SKPathMeasure

查看

native
层的
getLength()
源码:


 SkScalar SkPathMeasure::getLength() {
  if (fPath == NULL) {
   return 0;
  }
  if (fLength < 0) {
   this->buildSegments();
  }
  SkASSERT(fLength >= 0);
  return fLength;
}

实际上调用的

buildSegments()
来对
fLength
赋值,这里底层的设计有一个很聪明的地方——在初始化
SKPathMeasure
时对
fLength
做了特殊处理:


SkPathMeasure::SkPathMeasure(const SkPath& path, bool forceClosed) {
 fPath = &path;
 fLength = -1; // signal we need to compute it
 fForceClosed = forceClosed;
 fFirstPtIndex = -1;
 fIter.setPath(path, forceClosed);
}

fLength=-1
时我们需要计算,也就是说当还没有执行过
getLength()
方法时,
fLength
一直是-1,一旦执行则
fLength>=0
,则下一次就不会执行
buildSegments(),
这样避免了重复计算.

截取buildSegments()部分代码:


void SkPathMeasure::buildSegments() {
 SkPoint   pts[4];
 int    ptIndex = fFirstPtIndex;
 SkScalar  distance = 0;
 bool   isClosed = fForceClosed;
 bool   firstMoveTo = ptIndex < 0;
 Segment*  seg;
 
 fSegments.reset();
 bool done = false;
 do {
  switch (fIter.next(pts)) {
   case SkPath::kMove_Verb:
    ptIndex += 1;
    fPts.append(1, pts);
    if (!firstMoveTo) {
     done = true;
     break;
    }
    firstMoveTo = false;
    break;
   case SkPath::kLine_Verb: {
    SkScalar d = SkPoint::Distance(pts[0], pts[1]);
    SkASSERT(d >= 0);
    SkScalar prevD = distance;
    distance += d;
    if (distance > prevD) {
     seg = fSegments.append();
     seg->fDistance = distance;
     seg->fPtIndex = ptIndex;
     seg->fType = kLine_SegType;
     seg->fTValue = kMaxTValue;
     fPts.append(1, pts + 1);
     ptIndex++;
    }
   } break;
   case SkPath::kQuad_Verb: {
    SkScalar prevD = distance;
    distance = this->compute_quad_segs(pts, distance, 0, kMaxTValue, ptIndex);
    if (distance > prevD) {
     fPts.append(2, pts + 1);
     ptIndex += 2;
    }
   } break;
   case SkPath::kConic_Verb: {
    const SkConic conic(pts, fIter.conicWeight());
    SkScalar prevD = distance;
    distance = this->compute_conic_segs(conic, distance, 0, kMaxTValue, ptIndex);
    if (distance > prevD) {
     // we store the conic weight in our next point, followed by the last 2 pts
     // thus to reconstitue a conic, you'd need to say
     // SkConic(pts[0], pts[2], pts[3], weight = pts[1].fX)
     fPts.append()->set(conic.fW, 0);
     fPts.append(2, pts + 1);
     ptIndex += 3;
    }
   } break;
   case SkPath::kCubic_Verb: {
    SkScalar prevD = distance;
    distance = this->compute_cubic_segs(pts, distance, 0, kMaxTValue, ptIndex);
    if (distance > prevD) {
     fPts.append(3, pts + 1);
     ptIndex += 3;
    }
   } break;
   case SkPath::kClose_Verb:
    isClosed = true;
    break;
   case SkPath::kDone_Verb:
    done = true;
    break;
  }
 } while (!done);
 fLength = distance;
 fIsClosed = isClosed;
 fFirstPtIndex = ptIndex;

代码较长需要慢慢思考。

fIter
是一个
Iter
类型,在
SKPath.h
中的声明:



bool SkPathMeasure::nextContour() {
 fLength = -1;
 return this->getLength() > 0;
}

native
层对应的API是
PathMeasure.nextContour()

总结

以上就是Android开发之自定义CheckBox的全部内容,希望本文对大家开发Android有所帮助。

您可能感兴趣的文章:详解Android Checkbox的使用方法Android中ListView结合CheckBox实现数据批量选择(全选、反选、全不选)Android控件之CheckBox、RadioButton用法实例分析Android中自定义Checkbox组件实例android开发教程之自定义控件checkbox的样式示例android RadioButton和CheckBox组件的使用方法Android checkbox的listView(多选,全选,反选)具体实现方法Android CheckBox 的使用案例分析Android在listview添加checkbox实现原理与代码Android控件系列之CheckBox使用介绍


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android开发之自定义CheckBox

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android开发之自定义CheckBox

要实现的效果如下考虑到关键是动画效果,所以直接继承View。不过CheckBox的超类CompoundButton实现了Checkable接口,这一点值得借鉴。 下面记录一下遇到的问题,并从源码的角度解决。 问题一: 支持 wrap_con
2022-06-06

android开发教程之自定义控件checkbox的样式示例

主界面xml文件代码如下:2022-06-06

安卓(Android)开发之自定义饼状图

先来看看效果图先分析饼状图的构成,非常明显,饼状图就是一个又一个的扇形构成的,每个扇形都有不同的颜色,对应的有名字,数据和百分比。 经以上信息可以得出饼状图的最基本数据应包括:名字 数据值 百分比 对应的角度 颜色。用户关心的数据 : 名字
2022-06-06

Android开发之自定义控件用法详解

本文实例讲述了Android开发之自定义控件用法。分享给大家供大家参考,具体如下: 今天和大家分享下组合控件的使用。很多时候android自定义控件并不能满足需求,如何做呢?很多方法,可以自己绘制一个,可以通过继承基础控件来重写某些环节,当
2022-06-06

Android开发之自定义View(视图)用法详解

本文实例讲述了Android开发之自定义View(视图)用法。分享给大家供大家参考,具体如下: View类是Android的一个超类,这个类几乎包含了所有的屏幕类型。每一个View都有一个用于绘图的画布,这个画布可以进行任意扩展。在游戏开发
2022-06-06

Android开发之自定义数字键盘的示例

这篇文章给大家分享的是有关Android开发之自定义数字键盘的示例的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。效果图加载键盘存储键属性的XML描述我们下面的介绍都是依靠上图的实现来展开的,首先是软键盘的布局,我
2023-05-30

Android程序开发之自定义设置TabHost,TabWidget样式

先看效果:京东商城底部菜单栏新浪微博底部菜单栏本次学习效果图:第一,主布局文件(启动页main.xml,位于res/layout目录下)代码
2022-06-06

android开发教程之自定义属性用法详解

最近项目中经常需要用到自定义控件,因此自定义属性也是经常要用到的,在此说明一下自定义属性的用法: 自定义属性都存在于/value/attr.xml文件中,以如下格式存在。 代码如下:
2022-06-06

android自定义toast(widget开发)示例

1、Toast控件: 通过查看源代码,发现Toast里面实现的原理是通过服务Context.LAYOUT_INFLATER_SERVICE获取一个LayoutInflater布局管理器,从而获取一个View对象(TextView),设置内容
2022-06-06

Android自定义控件之自定义属性(二)

前言: 上篇介绍了自定义控件的基本要求以及绘制的基本原理,本篇文章主要介绍如何给自定义控件自定义一些属性。本篇文章将继续以上篇文章自定义圆形百分比为例进行讲解。有关原理知识请参考Android自定义控件基本原理详解(一)这篇文章。 需求产生
2022-06-06

Android开发中自定义editText下划线

这篇文章主要介绍了Android开发中自定义editText下划线的相关资料,需要的朋友可以参考下
2023-03-08

Android自定义TitleView标题开发实例

Android开发过程中,经常遇到一个项目需要重复的定义相同样式的标题栏,Android相继推出了actionBar, toolBar, 相信有用到的朋友也会遇到一些不如意的时候,比如标题栏居中时,需要自定义xml文件给toolBar等,不
2022-06-06

OpenHarmony应用开发之自定义弹窗

在应用的使用和开发中,弹窗是一个很常见的场景,自定义弹窗又因为极高的自由度得以广泛应用。本文以橘子购物中一个应用更新提示的弹窗介绍OpenHarmony的自定义弹窗。

iOS开发技巧之自定义相机

最近公司的项目中用到了相机,由于不用系统的相机,UI给的相机切图,必须自定义才可以。就花时间简单研究了一下相机的自定义。 相机属于系统硬件,这就需要我们来手动调用iPhone的相机硬件,分为以下步骤: 1、首先声明以下对象#import <
2022-05-19

Android自定义ViewGroup之CustomGridLayout(一)

之前写了两篇关于自定义view的文章,本篇讲讲自定义ViewGroup的实现。 我们知道ViewGroup就是View的容器类,我们经常用的LinearLayout,RelativeLayout等都是ViewGroup的子类。并且我们在写布
2022-06-06

Android自定义ViewGroup之WaterfallLayout(二)

上一篇我们学习了自定义ViewGroup的基本步骤,并做了一个CustomGridLayout的实例,这篇我们继续来说说自定义ViewGroup。 Android中当有大量照片需要展示的时候,我们可以用GridView作为照片墙,但是Gr
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录