`
modabobo
  • 浏览: 527419 次
文章分类
社区版块
存档分类
最新评论

Android 自定义控件 优雅实现元素间的分割线 (支持3.0以下)

 
阅读更多

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/42407923,本文出自:【张鸿洋的博客】

1、概述

话说,随着Android SDK版本的升级,很多控件增加了新的属性方便我们的使用,比如LinearLayout中多了:divider、showDividers等,用于为其内部元素添加分隔;但是呢,这样的属性在较低版本的SDK中不能被支持,那么,我们在开发过程中,可能会出现这样的需求:将这个新的特性想办法做到尽可能的向下兼容。有人说,可以自己写个新的控件去实现,这样的确可以,但是会不会太霸气了点。难道就没有接地气一点的方式么?嗯,本文就是这样的一个目的,以一种较为接地气的方式,实现新的属性的向下兼容。

这样的情况在Android中肯定会很多,希望可以以此进行抛砖引玉,大家遇到类似的情况,提供一定的思路。这才是这篇博客的真正目的!


2、divider相关用法

为了保证简介性,这里就不讨论divider有多么多么好用神马的,因为不是我们的重点。当然了这里提供一篇divider的参考:grid-spacing-on-android(基本就是引出divider的用处,有兴趣的看下,本文的demo样子也将参考本链接)。

大家先看一个效果图:


如果要实现,这样的效果图,对于这3个Button大家会怎么做(主要看button):

简单嘛:一个水平的线性布局,内部三个Button的weight都为1,然后第二个Button设置leftMargin,rightMargin就可以了。

嗯,没问题,假设现在我有一个需求:经过某个操作Button3隐藏,然后让Button1和Button2按如下布局:


这样的感觉是不是不错,虽然少了一个,完全不影响美观;但是,如果按照上述的答案

“一个水平的线性布局,内部三个Button的weight都为1,然后第二个Button设置leftMargin,rightMargin就可以了” Button2的右边会多出一个rightMargin 。

所以,这样的制作方式很明显不是最优秀的,最优秀的方案是,使用Linearlayout的divider、showDividers属性:

布局代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="20dp"
    android:layout_margin="10dp"
    android:background="#22444444"
    android:orientation="vertical" >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="128dp"
        android:background="@android:color/darker_gray"
        android:gravity="center"
        android:text="application_logo" />

    <LinearLayout
        android:id="@+id/buttons_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:divider="@drawable/divider"
        android:orientation="horizontal"
        android:showDividers="middle" >

        <Button
            android:id="@+id/btn_first"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="#ff0000"
            android:text="button_1" />

        <Button
            android:id="@+id/btn_second"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="#00ff00"
            android:text="button_2" />

        <Button
            android:id="@+id/btn_third"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="#0000ff"
            android:text="button_3" />
    </LinearLayout>

</LinearLayout>

其实核心就是放置Button的LinearLayout设置了android:divider="@drawable/divider"和android:showDividers="middle" ;

当然了,有人会说,我就是任性,我就用margin来实现,消失的时候,我显示去控制button的rightMargin为0也可以。嗯,是的,你不嫌麻烦的确没问题。那么现在问题又来了,我现在要求每个Button间的间隔是蓝色的,你怎么办?注意:我们这里的divider的值设置的是一个drawable噢~~没辙了吧。

本例的drawable(divider.xml):

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >
    <size android:width="15dp" />
    <solid android:color="@android:color/transparent" />
</shape>

下面简单介绍下divider、showDividers、dividerPadding:

divider可以设置一个drawable作为元素间的间隔;

showDividers:可取值为:middle(子元素间)、beginning(第一个元素左边)、end(最后一个元素右边)、none;【关于垂直方向的类似】

dividerPadding:设置绘制间隔元素的上下padding。

很简单,大家自己动手做下实验就知道了。

好了,到此,我们简单介绍了divider等的好处以及使用方式。但是这么优雅的来实现元素间的间隔只有在3.0以上才被支持,那么3.0以下怎么办呢?

别怕,下面开始本文的重点,让divider兼容至3.0一下。

3、自定义LinearLayout

看了标题,大家认为又是自定义LinearLayout么~~

嗯,继承LinearLayout是肯定的,我们没有办法改变它的源码,但是可以通过继承去改变一些特性。

注意下:现在的目的是兼容至3.0以下:

首先看一个3.0以下的效果图,不然你说我骗你:


上面的布局文件在3.0以下显示就是这么个样子,完全无视间隔。

首先考虑一个问题,对于divider、showDividers 3.0以下的LinearLayout肯定无视呀,咋办呢?

我们实现个LinearLayout的子类,让它认识divider和showDividers~~~重视一下这里,这里就是我们向前迈进的一大步,以后遇到类似问题,都这么干。

1、识别高版本的属性

public class IcsLinearLayout extends LinearLayout
{
	private static final int[] LL = new int[]
	{ //
		android.R.attr.divider,//
			android.R.attr.showDividers,//
			android.R.attr.dividerPadding //
	};

	private static final int LL_DIVIDER = 0;
	private static final int LL_SHOW_DIVIDER = 1;
	private static final int LL_DIVIDER_PADDING = 2;

	/**
	 * android:dividers
	 */
	private Drawable mDivider;
	/**
	 * 对应:android:showDividers
	 */
	private int mShowDividers;
	/**
	 * 对应:android:dividerPadding
	 */
	private int mDividerPadding;
	
	private int mDividerWidth;
	private int mDividerHeight;

	public IcsLinearLayout(Context context, AttributeSet attrs)
	{
		super(context, attrs);

		TypedArray a = context.obtainStyledAttributes(attrs, LL);
		setDividerDrawable(a.getDrawable(IcsLinearLayout.LL_DIVIDER));
		mDividerPadding = a.getDimensionPixelSize(LL_DIVIDER_PADDING, 0);
		mShowDividers = a.getInteger(LL_SHOW_DIVIDER, SHOW_DIVIDER_NONE);
		a.recycle();
	}
	
	/**
	 * 设置分隔元素,初始化宽高等
	 */
	public void setDividerDrawable(Drawable divider)
	{
		if (divider == mDivider)
		{
			return;
		}
		mDivider = divider;
		if (divider != null)
		{
			mDividerWidth = divider.getIntrinsicWidth();
			mDividerHeight = divider.getIntrinsicHeight();
		} else
		{
			mDividerWidth = 0;
			mDividerHeight = 0;
		}
		setWillNotDraw(divider == null);
		requestLayout();
	}


这里贴出了成员变量和我们的构造方法,成员变量中包含了3个属性对应的接收变量;然后我们在构造里面对这三个属性进行了获取并赋值给相应的属性;

这里大家肯定会困惑,我上面定义了一个整型数组,然后几个变量为数组下标,最后利用这个数组和下标在构造里面获取了值。是不是要问,你为什么这么写,你咋知道的?

嗯,这样,大家随便下载我之前包含自定义属性的文章,或者你自己写的:

这里我拿了Android BitmapShader 实战 实现圆形、圆角图片这个例子中的源代码,大家就不用下载了,看看我下面就明白了,我在这里例子中自定义了两个属性:type和border_radius,看看我们的R.java里面生成了什么样的代码:

   public static final int border_radius=0x7f010001;
   public static final int type=0x7f010000;

    public static final int[] RoundImageViewByShader = {
            0x7f010000, 0x7f010001
        };
  public static final int RoundImageViewByShader_type = 0;
  public static final int RoundImageViewByShader_border_radius = 1;
  

看见木有,整型数组,下标;我们的android.R.attr.xxx对应于上面的常量。是不是和我们上例定义的一模一样~~

对,自定义属性怎么获取的,你照着模仿就是,无非现在的属性是android.R.attr.xxx而不是你自定义的,本质没区别。

好了,现在大家应该知道怎么获取高版本的属性了~~

2、onMeasure

获取到分隔元素以后,分隔元素肯定有宽和高,我们这里把分隔元素的宽和高转化为合适的margin

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
	{
		//将分隔元素的宽高转化为对应的margin
		setChildrenDivider();
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	}

	/**
	 * 将分隔元素的宽高转化为对应的margin
	 */
	protected void setChildrenDivider()
	{
		final int count = getChildCount();
		for (int i = 0; i < count; i++)
		{
			//遍历每个子View
			View child = getChildAt(i);
			//拿到索引
			final int index = indexOfChild(child);
			//方向
			final int orientation = getOrientation();
		
			final LayoutParams params = (LayoutParams) child.getLayoutParams();
			//判断是否需要在子View左边绘制分隔
			if (hasDividerBeforeChildAt(index))
			{
				if (orientation == VERTICAL)
				{
					//如果需要,则设置topMargin为分隔元素的高度(垂直时)
					params.topMargin = mDividerHeight;
				} else
				{
					//如果需要,则设置leftMargin为分隔元素的宽度(水平时)
					params.leftMargin = mDividerWidth;
				}
			}
		}
	}
	
	/**
	 * 判断是否需要在子View左边绘制分隔
	 */
	public boolean hasDividerBeforeChildAt(int childIndex)
	{
		if (childIndex == 0 || childIndex == getChildCount())
		{
			return false;
		}
		if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0)
		{
			boolean hasVisibleViewBefore = false;
			for (int i = childIndex - 1; i >= 0; i--)
			{
				//当前index的前一个元素不为GONE则认为需要
				if (getChildAt(i).getVisibility() != GONE)
				{
					hasVisibleViewBefore = true;
					break;
				}
			}
			return hasVisibleViewBefore;
		}
		return false;
	}

onMeasure中,将divider的宽和高,根据mShowDividers的情况,设置给了合适的View的margin;

其实就是,将divider需要占据的地方,利用margin空出来,我们最后会在这个空的区域进行绘制divider,别忘了,我们的divider是个drawable。

3、onDraw

好了,既然已经通过margin把需要绘制的地方空出来了,那么下面就是绘制了~~~

@Override
	protected void onDraw(Canvas canvas)
	{

		if (mDivider != null)
		{
			if (getOrientation() == VERTICAL)
			{
				//绘制垂直方向的divider
				drawDividersVertical(canvas);
			} else
			{
				//绘制水平方向的divider
				drawDividersHorizontal(canvas);
			}
		}
		super.onDraw(canvas);
	}

	/**
	 * 绘制水平方向的divider
	 * @param canvas
	 */
	private void drawDividersHorizontal(Canvas canvas)
	{
		final int count = getChildCount();
		//遍历所有的子View
		for (int i = 0; i < count; i++)
		{
			final View child = getChildAt(i);

			if (child != null && child.getVisibility() != GONE)
			{
				//如果需要绘制divider
				if (hasDividerBeforeChildAt(i))
				{
					final android.widget.LinearLayout.LayoutParams lp = (android.widget.LinearLayout.LayoutParams) child
							.getLayoutParams();
					//得到开始的位置,getLeft为当前View的左侧,而左侧有margin,所以之差为divider绘制的开始区域
					final int left = child.getLeft() - lp.leftMargin/*
																	 * -
																	 * mDividerWidth
																	 */;
					//绘制divider
					drawVerticalDivider(canvas, left);
				}
			}
		}
	}
	
	/**
	 * 绘制divider,根据left,水平方向绘制
	 * @param canvas
	 * @param left
	 */
	public void drawVerticalDivider(Canvas canvas, int left)
	{
		//设置divider的范围
		mDivider.setBounds(left, getPaddingTop() + mDividerPadding, left
				+ mDividerWidth, getHeight() - getPaddingBottom()
				- mDividerPadding);
		//绘制
		mDivider.draw(canvas);
	}

为了代码的简短以及帮助大家的理解,这里没有贴出垂直方向的,水平方向的整个流程是完整的 。后面会贴出来垂直方向的绘制代码。

其实也比较简单,在onDraw里面判断方向,这里以水平为例:遍历所有的子View,如果发现需要在其前绘制divider的,则算出divider的开始的位置(child.getLeft() - lp.leftMargin),然后调用drawVerticalDivider(),设置divider范围,紧接着绘制出来。

垂直方向同理,就不赘述了,贴上代码:

	private void drawDividersVertical(Canvas canvas)
	{
		final int count = getChildCount();
		for (int i = 0; i < count; i++)
		{
			final View child = getChildAt(i);

			if (child != null && child.getVisibility() != GONE)
			{
				if (hasDividerBeforeChildAt(i))
				{
					final android.widget.LinearLayout.LayoutParams lp = (android.widget.LinearLayout.LayoutParams) child
							.getLayoutParams();
					final int top = child.getTop() - lp.topMargin/*
																 * -
																 * mDividerHeight
																 */;
					drawHorizontalDivider(canvas, top);
				}
			}
		}
	}
	private void drawHorizontalDivider(Canvas canvas, int top)
	{
		mDivider.setBounds(getPaddingLeft() + mDividerPadding, top, getWidth()
				- getPaddingRight() - mDividerPadding, top + mDividerHeight);
		mDivider.draw(canvas);
	}

代码说完了,下面干嘛呢?当然是测试了~~不测试怎么知道结果~~

4、测试

首先我们把布局文件中包含Button的Linelayout换成我们的com.zhy.view.IcsLinearLayout

在3.0以下机子上运行:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="20dp"
    android:layout_margin="10dp"
    android:background="#22444444"
    android:orientation="vertical" >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="128dp"
        android:background="@android:color/darker_gray"
        android:gravity="center"
        android:text="application_logo" />

    <com.zhy.view.IcsLinearLayout
        android:id="@+id/buttons_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:divider="@drawable/divider"
        android:orientation="horizontal"
        android:showDividers="middle" >

        <Button
            android:id="@+id/btn_first"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="#ff0000"
            android:text="button_1" />

        <Button
            android:id="@+id/btn_second"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="#00ff00"
            android:text="button_2" />

        <Button
            android:id="@+id/btn_third"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="#0000ff"
            android:text="button_3" />
    </com.zhy.view.IcsLinearLayout>

</LinearLayout>

效果图:


久违了~~我们的分隔~~可以看到在3.0以下机器完美实现~~~

but,别高兴太早,我们这么改,3.0以上机器是什么样子呢?


哈哈,是不是完美实现了间隔~~~

现在可以高兴了~~~

大家现在肯定有困惑,我擦,你在构造里面获取divider,然后在onDraw里面自己绘制了divider,大家都知道3.0以上是支持的呀,肯定也会绘制呀,你说没冲突谁信呀~~~!!!

5、答疑

1、为什么和3.0以上没有发生一些该有的冲突?

嗯,是的,3.0以上是支持的,为什么我们在onDraw里面自己绘制,然后调用super.onDraw竟然没有发生什么冲突?

原因很简单:我们看4.4LinearLayout的源码:

@Override
    protected void onDraw(Canvas canvas) {
        if (mDivider == null) {
            return;
        }

其实,源码中也是在onDraw里面去绘制divider,但是如果mDivider为null,就会return。之所以没有冲突,是因为我们前面的某个操作让其mDivider成员变量为null了~~

现在去LinearLayout的构造方法:

public LinearLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        ...
        setDividerDrawable(a.getDrawable(R.styleable.LinearLayout_divider));
        ...
    }
  public void setDividerDrawable(Drawable divider) {
        if (divider == mDivider) {
            return;
        }
        mDivider = divider;
        if (divider != null) {
            mDividerWidth = divider.getIntrinsicWidth();
            mDividerHeight = divider.getIntrinsicHeight();
        } else {
            mDividerWidth = 0;
            mDividerHeight = 0;
        }
        setWillNotDraw(divider == null);
        requestLayout();
    }

可以看到它在其构造中调用setDividerDrawable为其mDivider赋值,关键来了~~~~我们的自定义的LinearLayout复写了这个方法,也就是说,setDividerDrawable会调用子类的方法,这个父类的setDividerDrawable根本不会调用,从而导致mDivider为null了~~~

为null就对应了onDraw里面的绘制~~ok~解答完毕。

2、这篇博客怎么想到的?你咋知道代码这么写?

我相信这样的问题,很多人感兴趣,其实也算巧合,之前知道有divider这个属性;然后前段时间写Android 教你打造炫酷的ViewPagerIndicator 不仅仅是高仿MIUI这篇博客的时候,特意去看了ViewPagerIndicator那个开源项目源码,发现了一个IcsLinearLayout这样的一个类,类似我们上面实现的,当然了,我做了一定的修改;于是乎,仔细研究了这了类,觉得很有必要写成博客,达到文章开头所叙述的的目的~其实大家有心的话,根据我们上述的代码,去看看LinearLayout源码中如何去绘制divider,你会发现代码基本是一样的(ps:你没发现问题1中的LinearLayout源码的setDividerDrawable和我们写的一模一样么~);


好了,到此整篇文章就结束了,还是那句话:”这样的情况在Android中肯定会很多,希望可以以此进行抛砖引玉,大家遇到类似的情况,提供一定的思路。这才是这篇博客的真正目的!“ 不要偷懒花点时间去敲一敲,看一看,想一想,你会发现里面还藏着很多东西,别怕浪费时间,我研究和写这篇博客的时间绝对超出你所学习这篇博客的时间~~


最后,欧来来~~



源码点击下载



建了一个,方便大家交流。群号:423372824

----------------------------------------------------------------------------------------------------------

博主部分视频已经上线,如果你不喜欢枯燥的文本,请猛戳(初录,期待您的支持):

1、Android中百度地图的使用

2、Android 自定义控件实战 电商活动中的刮刮卡

3、Android自定义控件实战 打造Android流式布局和热门标签

4、Android智能机器人“小慕”的实现

5、高仿QQ5.0侧滑

6、高仿微信5.2.1主界面及消息提醒







分享到:
评论

相关推荐

    Android自定义控件开发入门与实战.zip

    在Android应用开发中,自定义控件是提升用户体验和实现独特设计的重要手段。《Android自定义控件开发入门与实战》这本书深入浅出地讲解了如何在Android平台上创建和使用自定义控件,旨在帮助开发者从基础知识到实战...

    《Android自定义控件开发入门与实战》_启舰.rar

    Android自定义控件开发入门与实战从自定义基础到实战的讲解。一步步深入。适合有一定Android基础的读者。本压缩包中自带了推荐的pdf阅读器。大家要是喜欢这本文档,推荐去京东,天猫,当当买支持一下默默付出的作者...

    android自定义控件介绍,重写控件

    在Android开发中,自定义控件是提升应用用户体验和实现独特设计的重要手段。本文将深入探讨Android自定义控件的概念、重要性以及如何通过重写已有控件来扩展其功能,帮助开发者从初阶迈进高阶。 首先,我们了解什么...

    android 自定义控件实现demo收集 及 框架收集

    下面将对"android 自定义控件实现demo收集 及 框架收集"这一主题进行深入探讨。 首先,自定义控件在Android应用开发中扮演着重要角色。它们允许开发者根据项目需求创建具有独特功能和外观的组件,超越了Android SDK...

    Android自定义控件示例

    在Android开发中,自定义控件是提升应用独特性和功能扩展性的重要手段。这个压缩包“CustomViews”很可能是包含了一系列Android自定义控件的示例项目,旨在帮助开发者理解和学习如何在Android Studio 1.0.2环境下...

    Android自定义控件实现导航条IndicatorView

    在Android应用开发中,自定义控件是提升用户体验和界面个性化的重要手段。本文将深入探讨如何实现一个自定义的导航条IndicatorView,该控件具备滚动、快速滚动以及自动滚动到选中项完全显示在屏幕上的功能。我们将...

    android 自定义控件源码实现

    本文将深入探讨如何在Android中实现自定义控件,特别是针对"TestImgTextButton"这个自定义按钮的源码分析。 首先,我们要了解自定义控件的基本流程。自定义控件通常包括以下几个步骤: 1. **创建新类**:自定义...

    Android 自定义控件 组合控件

    总结起来,Android自定义组合控件的实现涉及到了对Android UI框架的深入理解和实践,包括继承自定义View或ViewGroup、测量与布局、绘制、事件处理等关键步骤。通过这样的方式,开发者可以构建出功能强大、交互丰富的...

    安卓自定义控件相关-Android自定义控件源码.rar

    这个压缩包"Android自定义控件源码.rar"包含了一些自定义控件的源代码,虽然不能保证每个都可直接运行,但它们提供了丰富的学习资源,帮助开发者理解和实践自定义控件的创建过程。下面将详细探讨Android自定义控件的...

    【Android进阶】(1)用继承和组合方式自定义控件

    在Android开发中,自定义控件是提升应用用户体验和界面设计独特性的重要手段。本教程主要探讨如何通过继承和组合的方式来自定义控件,适用于已经有一定Android基础的开发者进行进阶学习。 首先,我们来理解自定义...

    Android自定义控件(实现状态提示图表)

    总的来说,通过自定义控件,开发者可以实现复杂且富有交互性的界面元素,增强用户与应用的互动体验。这个状态提示图表的例子展示了Android自定义控件的基本原理和实现步骤,对初学者来说是一次很好的实践。通过理解...

    Android 自定义控件简单Demo

    本示例"Android 自定义控件简单Demo"将向我们展示如何创建一个结合图片和文字的自定义控件,这通常用于构建定制化的按钮、标签或其他UI元素。自定义控件允许开发者根据项目需求自由设计界面,实现独特的交互效果。 ...

    Android自定义控件的demo

    本示例将深入讲解如何基于Android系统实现一个自定义的Button控件,该控件由一个ImageView和一个TextView组成,并添加了标签功能。以下我们将从以下几个方面展开讨论: 1. **基础知识**:在Android中,控件是构成UI...

    《Android自定义控件入门到实战》源码2018.10

    《Android自定义控件入门到实战》是一本深入讲解Android平台下自定义控件开发的教程,源码2018.10版提供了一套完整的实践案例,帮助开发者从基础到进阶全面掌握自定义控件的制作技巧。这份资料涵盖了从基本的自定义...

    《Android自定义控件入门到实战》源码

    《Android自定义控件入门到实战》源码提供了一套完整的自定义控件学习资源,涵盖了从基础到高级的各种实例,帮助开发者深入理解和实践Android自定义控件的开发。 自定义控件的核心在于扩展Android内置的View或...

    自定义控件Demo

    在Android开发中,自定义控件是提升应用界面独特性和用户体验的重要手段。通过自定义控件,开发者可以创建出符合自身需求的UI组件,从而更好地实现应用的功能和设计。本Demo着重介绍了如何在Android中创建自定义控件...

    Android简单实现自定义控件

    在Android开发中,自定义控件是提升应用独特性和用户体验的有效方式。本教程将引导初学者如何简单地创建一个自定义控件,该控件基于`LinearLayout`进行扩展。通过这个过程,我们可以深入理解Android UI组件的工作...

    Android自定义控件之拖动条

    在Android开发中,自定义控件是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何创建一个美观的自定义拖动条控件,即"Android自定义控件之拖动条"。我们将讨论以下几个关键知识点: 1. **基础知识**:...

    Android 自定义组合控件案例

    以下将详细介绍Android自定义组合控件的相关知识点。 一、自定义控件的分类 1. 组件扩展:对现有控件进行功能增强或样式修改,例如自定义Button增加动画效果。 2. 组合控件:结合多个基础控件,形成新的复合控件,...

    android 自定义控件 小球圆周运动

    在Android开发中,自定义控件是实现独特用户界面效果的重要手段。本教程将深入探讨如何创建一个自定义控件,让小球沿着圆周进行运动。这个过程涉及到Canvas绘图、动画处理以及Android帧率控制等多个核心知识点。 ...

Global site tag (gtag.js) - Google Analytics