您的位置:首页 > 移动开发 > Android开发

Android开发-自定义View-AndroidStudio(一)

2016-12-13 16:53 253 查看
转载请注明出处:http://blog.csdn.net/iwanghang/article/details/53611277
觉得博文有用,请点赞,请评论,请关注,谢谢!~
项目源码:http://download.csdn.net/detail/iwanghang/9710791

直接看这个博文有点难懂,建议新手先看看这篇:

Android开发-自定义View-AndroidStudio(二十一)onDraw的演示:
http://blog.csdn.net/iwanghang/article/details/54018759
Android开发-自定义View-AndroidStudio(八)自定义View初体验:
http://blog.csdn.net/iwanghang/article/details/53783417

本篇博文代码,直接是从 鸿洋_ 博客复制粘贴的,但又不仅仅是 复制粘贴。查资料,用了半天时间,加入了大量注释。
在我们从Android小学生 进阶到 Android中学生的路上,肯定需要经历 自定义View。
大神已经给出了,很完整的一个Demo,我这里 顺水推舟,把一些基础知识 标记在代码之中,各位可以一边看源码 一边学基础。
觉得文章有用,请留言~请点赞~
直接上GIF效果图和代码:



DynamicButton.java:
package com.iwanghang.myview.view;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.widget.Toast;

import com.iwanghang.myview.R;

public class CustomTitleView extends View
{
/**
* 文本
*/
private String mTitleText;
/**
* 文本的颜色
*/
private int mTitleTextColor;
/**
* 文本的大小
*/
private int mTitleTextSize;

/**
* 绘制时控制文本绘制的范围
*/
private Rect mBound;
private Paint mPaint;

public CustomTitleView(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}

public CustomTitleView(Context context)
{
this(context, null);
}

/**
* 获得我自定义的样式属性
*
* @param context 上下文。
* @param attrs 属性集合。xml中你可以按照这个集合赋值该有的属性;在自定义的控件中,可以获取这些值来各种绘制。
*              是接收xml中定义的属性信息,这不一定是自定义布局,不是自定义布局也有该属性,
*              要不xml中定义的属性信息就无法接收了。
* @param defStyle 这个参数是用来指定view的默认style的,如果是0,那么将不会应用任何默认(或者叫缺省)的style。
*                 另外这个参数可以是一个属性指定的style引用,也可以直接是一个显式的style资源。
*                 指定为0的话会影响到继承该view的子类,会导致初始化时的一些资源读不到,导致空指针。
*/
public CustomTitleView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
/**
* 获得我们所定义的自定义样式属性
*/
/**
* obtainStyledAttributes 函数获取属性:
* 1、obtainAttributes(AttributeSet set, int[] attrs) // 从layout设置的属性集中获取attrs中的属性
* 2、obtainStyledAttributes(int[] attrs) // 从系统主题中获取attrs中的属性
* 3、obtainStyledAttributes(int resId,int[] attrs) // 从资源文件定义的style中读取属性
* 4、obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
*
* 我们用的是4。它有4个参数,参数说明如下:
* 4.1、set:属性值的集合
* 4.2、attrs:我们要获取的属性的资源ID的一个数组,如同ContextProvider中请求数据库时的Projection数组,
* 就是从一堆属性中我们希望查询什么属性的值
* 4.3、defStyleAttr:这个是当前Theme中的一个attribute,是指向style的一个引用,当在layout xml中和
* style中都没有为View指定属性时,会从Theme中这个attribute指向的Style中查找相应的属性值,这就是defStyle
* 的意思,如果没有指定属性值,就用这个值,所以是默认值,但这个attribute要在Theme中指定,且是指向一个Style
* 的引用,如果这个参数传入0表示不向Theme中搜索默认值
* 4.4、defStyleRes:这个也是指向一个Style的资源ID,但是仅在defStyleAttr为0或defStyleAttr不为0但Theme中
* 没有为defStyleAttr属性赋值时起作用
*/
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyle, 0);
int n = typedArray.getIndexCount();
for (int i = 0; i < n; i++)
{
int attr = typedArray.getIndex(i);
switch (attr)
{
case R.styleable.CustomTitleView_titleText:
mTitleText = typedArray.getString(attr);
break;
case R.styleable.CustomTitleView_titleTextColor:
// 默认颜色设置为黑色
mTitleTextColor = typedArray.getColor(attr, Color.BLACK);
break;
case R.styleable.CustomTitleView_titleTextSize:
// 默认设置为16sp,TypeValue也可以把sp转化为px
mTitleTextSize = typedArray.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break;

}

}
/**
* 回收TypedArray,以便后面重用。在调用这个函数后,你就不能再使用这个TypedArray。
* 在TypedArray后调用recycle主要是为了缓存。当recycle被调用后,这就说明这个对象从现在可以被重用了。
*/
typedArray.recycle(); // 回收TypedArray

/**
* 获得绘制文本的宽和高
*/
mPaint = new Paint(); // 实例化 画笔
mPaint.setTextSize(mTitleTextSize);
// mPaint.setColor(mTitleTextColor);
mBound = new Rect(); // 实例化 矩形
/**
* getTextBounds的四个参数:
* 1、text 字符串来衡量并返回它的界限
* 2、start 索引的第一个字符的字符串来衡量
* 3、end 过去的最后一个字符字符串
* 4、Rect 返回联合边界的所有文本。必须分配给调用者。
*/
mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);

this.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
mTitleText = randomText(); // 文字 = 随机数
Toast.makeText(getContext(), "随机数 = " + mTitleText , Toast.LENGTH_SHORT).show();
postInvalidate(); // 使用postInvalidate则比较简单,不需要handler,直接在线程中调用postInvalidate即可。
}

});

}

/**
* 随机数
*/
private String randomText()
{
Random random = new Random();
Set<Integer> set = new HashSet<Integer>();
while (set.size() < 4)
{
int randomInt = random.nextInt(10);
set.add(randomInt);
}
StringBuffer sb = new StringBuffer();
for (Integer i : set)
{
sb.append("" + i);
}
return sb.toString();
}

/**
* 重载onMeasure(),onLayout(),onDraw()三个函数构建了自定义View的外观形象。
* 加上onTouchEvent()等重载视图的行为,可以构建任何我们需要的可感知到的自定义View。
*
* 1.View本身大小多少,这由onMeasure()决定;2.View在ViewGroup中的位置如何,这由
* onLayout()决定;3.绘制View,onDraw()定义了如何绘制这个View。
*
* 决定View的大小只需要两个值:宽 详细 测量值(widthMeasureSpec)和高 详细 测量值(heightMeasureSpec)。
* 对于详细测量值( measureSpec )需要两样东西来确定它,那就是大小(size)和模式(mode)。
*
* @param widthMeasureSpec 宽 详细 测量值
* @param heightMeasureSpec 高 详细 测量值
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);

int width = 0;
int height = 0;

/**
* 设置宽度
* MeasureSpec.EXACTLY:精确模式。在这种模式下,尺寸的值是多少,那么这个组件的长或宽就是多少。
*                      父视图希望子视图的大小应该是specSize中指定的。
* MeasureSpec.AT_MOST:最大模式。这个也就是父组件,能够给出的最大的空间,当前组件的长或宽最大只能为这么大,当然也可以比这个小。
*                      子视图的大小最多是specSize中指定的值,也就是说不建议子视图的大小超过specSize中给定的值。
* MeasureSpec.UNSPECIFIED:未指定模式。这个就是说,当前组件,可以随便用空间,不受限制。
*                          我们可以随意指定视图的大小。
*/
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
switch (specMode)
{
case MeasureSpec.EXACTLY:// 明确指定了
width = getPaddingLeft() + getPaddingRight() + specSize;
break;
case MeasureSpec.AT_MOST:// 一般为WARP_CONTENT
width = getPaddingLeft() + getPaddingRight() + mBound.width();
break;
}

/**
* 设置高度
*/
specMode = MeasureSpec.getMode(heightMeasureSpec);
specSize = MeasureSpec.getSize(heightMeasureSpec);
switch (specMode)
{
case MeasureSpec.EXACTLY:// 明确指定了
height = getPaddingTop() + getPaddingBottom() + specSize;
break;
case MeasureSpec.AT_MOST:// 一般为WARP_CONTENT
height = getPaddingTop() + getPaddingBottom() + mBound.height();
break;
}
/**
* setMeasuredDimension 设置自定义的控件的大小
*
* setMeasuredDimension函数是一个很关键的函数,它对View的成员变量mMeasuredWidth
* 和mMeasuredHeight变量赋值,而measure的主要目的就是对View树中的每个View的
* mMeasuredWidth和mMeasuredHeight进行赋值,一旦这两个变量被赋值,则意味着该View的测量工作结束。
*
* 在onMeasure(int, int)中,必须调用setMeasuredDimension(int width, int height)来存储测量得到的
* 宽度和高度值,如果没有这么去做会触发异常IllegalStateException。
*/
setMeasuredDimension(width, height);

}

@Override
protected void onDraw(Canvas canvas)
{
mPaint.setColor(Color.YELLOW);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);

mPaint.setColor(mTitleTextColor);
canvas.drawText(mTitleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
}
}
arrts.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="titleText" format="string" />
<attr name="titleTextColor" format="color" />
<attr name="titleTextSize" format="dimension" />

<declare-styleable name="CustomTitleView">
<attr name="titleText" />
<attr name="titleTextColor" />
<attr name="titleTextSize" />
</declare-styleable>
</resources>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:custom="http://schemas.android.com/apk/res-auto"
tools:context="com.iwanghang.myview.MainActivity">

<com.iwanghang.myview.view.CustomTitleView
android:layout_width="200dp"
android:layout_height="100dp"
custom:titleText="3712"
custom:titleTextColor="#ff0000"
custom:titleTextSize="40sp" />

</RelativeLayout>

原文:Android 自定义View (一):http://blog.csdn.net/lmj623565791/article/details/24252901

转载请注明出处:http://blog.csdn.net/iwanghang/article/details/53611277
觉得文章有用,请留言~请点赞~
项目源码:http://download.csdn.net/detail/iwanghang/9710791

欢迎移动开发爱好者交流
沈阳或周边城市公司有意开发Android,请与我联系
联系方式



微信:iwanghang
QQ:413711276
邮箱:iwanghang@qq.com


觉得博文有用,请点赞,请评论,请关注,谢谢!~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息