“丢失树木”通过精心收集,向本站投稿了6篇Android自定义View的一些不知道的细节,以下是小编整理后的Android自定义View的一些不知道的细节,希望能够帮助到大家。

篇1:Android自定义View的一些不知道的细节
按照源码的注释,View的绘制过程是这样子的:
/* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: * *1. Draw the background *2. If necessary, save the canvas' layers to prepare for fading *3. Draw view's content *4. Draw children *5. If necessary, draw the fading edges and restore layers *6. Draw decorations (scrollbars for instance) */
先绘制背景,如果有必要,保存画布层,绘制内容,绘制孩子,绘制渐变,绘制装饰物。让我们再看下源码的draw方法里的一段代码
if (!verticalEdges && !horizontalEdges) {// Step 3, draw the contentif (!dirtyOpaque) onDraw(canvas);// Step 4, draw the childrendispatchDraw(canvas);// Step 6, draw decorations (scrollbars)onDrawScrollBars(canvas);// we're done...return; }
从这里我们可以看出draw方法的调用过程是这样子,
onDraw ->dispatchDraw ->onDrawScrollBars
这样我们基本可以明白我们重写的onDraw方法是干什么的了,以及draw() ondraw() dispatchdraw的关系了。如果你想在最顶层绘制,直接重写draw方法,因为draw方法里包含ondraw和dipatchdraw。其实主要想写的一个细节就是这个东西。如果你想让你绘制的东西一直在控件的某个位置一定要这样写canvas.translate(getScrollX(), getScrollY());
这里就不解释了,明白了我之前说的就知道为什么要这么写了。我重写了listview然后发现我写的控件会出现跳帧现象,就是在滑动的时候出现fading edges的时候,后来才发现是因为这个原因。
篇2:Android 开发自定义View
【附:——自定义View常处理的回调函数
onFinishInflate() 当View中所有的子控件均被映射成xml后触发
onMeasure(int, int) 确定所有子元素的大小
onLayout(boolean, int, int, int, int) 当View分配所有的子元素的大小和位置时触发
onSizeChanged(int, int, int, int) 当view的大小发生变化时触发
onDraw(Canvas) view渲染内容的细节
onKeyDown(int, KeyEvent) 有按键按下后触发
onKeyUp(int, KeyEvent) 有按键按下后弹起时触发
onTrackballEvent(MotionEvent) 轨迹球事件
onTouchEvent(MotionEvent) 触屏事件
onFocusChanged(boolean, int, Rect) 当View获取或失去焦点时触发
onWindowFocusChanged(boolean) 当窗口包含的view获取或失去焦点时触发
onAttachedToWindow() 当view被附着到一个窗口时触发
onDetachedFromWindow() 当view离开附着的窗口时触发,该方法和 onAttachedToWindow() 是相反,
onWindowVisibilityChanged(int) 当窗口中包含的可见的view发生变化时触发】
1、继承View类,重载父类方法(根据自己的需要进行重载)
package sn.dolp.view;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;public class XmlView extends View { public float cx = 40; public float cy = 50; Paint paint = new Paint(); public XmlView(Context context) { super(context); // TODO Auto-generated constructor stub } public XmlView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } public XmlView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); paint.setColor(Color.RED); canvas.drawCircle(cx, cy, 15, paint); } @Override public boolean onTouchEvent(MotionEvent event) { cx = event.getX(); cy = event.getY(); invalidate(); return true; }}
2、修改XML布局
参考文献:UUTON的文章,www.uuton.com/post/3f493_24c2c7,11月27日19:12:49

篇3:Android 刮刮卡自定义view
import android.content.Context;import android.graphics.Bitmap;import android.graphics.Bitmap.Config;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PorterDuff.Mode;import android.graphics.PorterDuffXfermode;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;public class TextClear extends View { private boolean isMove = false; private Bitmap bitmap = null; private Bitmap frontBitmap = null; private Path path; private Canvas mCanvas; private Paint paint; private Thread mSweapThread; private boolean isThreadStart = false; private OnTextClearListener mOnTextClearListener; private TextClear mTextClear; private boolean mComplete = false; public TextClear(Context context, AttributeSet attrs) { super(context, attrs); } public TextClear(Context context) { super(context); } /** * 重置刮奖图层 */ public void resetting { mCanvas = null; path = null; isThreadStart = false; mComplete = false; invalidate(); mTextClear.setVisibility(View.VISIBLE); } public void setOnTextClearListener(OnTextClearListener listener, TextClear textClear) { mOnTextClearListener = listener; mTextClear = textClear; } @Override protected void onDraw(Canvas canvas) { if (mCanvas == null) {EraseBitmp(); } canvas.drawBitmap(bitmap, 0, 0, null); mCanvas.drawPath(path, paint); super.onDraw(canvas); } public void EraseBitmp() { bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_4444); frontBitmap = CreateBitmap(Color.GRAY, getWidth(), getHeight()); paint = new Paint(); paint.setStyle(Paint.Style.STROKE); paint.setXfermode(new PorterDuffXfermode(Mode.CLEAR)); paint.setAntiAlias(true); paint.setDither(true); paint.setStrokeJoin(Paint.Join.ROUND); paint.setStrokeCap(Paint.Cap.ROUND); paint.setStrokeWidth(40); path = new Path(); mCanvas = new Canvas(bitmap); mCanvas.drawBitmap(frontBitmap, 0, 0, null); mSweapThread = new Thread(mRunnable); //图层初始化回调 if (mOnTextClearListener != null) {mOnTextClearListener.preStratchInitListener(mTextClear); } } @Override public boolean onTouchEvent(MotionEvent event) { float ax = event.getX(); float ay = event.getY(); if (event.getAction() == MotionEvent.ACTION_DOWN) {isMove = false;path.reset();path.moveTo(ax, ay);invalidate();return true; } else if (event.getAction() == MotionEvent.ACTION_MOVE) {isMove = true;path.lineTo(ax, ay);invalidate();return true; } else if (event.getAction() == MotionEvent.ACTION_UP) {if (!isThreadStart) { mSweapThread.start(); isThreadStart = true; //开始刮回调 if (mOnTextClearListener != null) { mOnTextClearListener.onStratchStartListener(mTextClear); }} } return super.onTouchEvent(event); } public Bitmap CreateBitmap(int color, int width, int height) { LogGloble.d(color, color + ); int[] rgb = new int[width * height]; for (int i = 0; i < rgb.length; i++) {rgb[i] = color; } return Bitmap.createBitmap(rgb, width, height, Config.ARGB_8888); } //刮奖完成回调接口 public interface OnTextClearListener { /** * 监听刮刮卡图层初始化 */ public void preStratchInitListener(TextClear textClear); /** * 监听刮的动作开始 */ public void onStratchStartListener(TextClear textClear); /** * 监听刮刮乐完成 */ public void onStratchCompleteListenner(TextClear textClear); } private Runnable mRunnable = new Runnable() { @Override public void run() {while (!mComplete) { int w = bitmap.getWidth(); int h = bitmap.getHeight(); float wipeArea = 0; float totalArea = w * h; int[] mPixels = new int[w * h]; bitmap.getPixels(mPixels, 0, w, 0, 0, w, h); for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) {int index = i + j * w;if (mPixels[index] == 0) { wipeArea++;} } } if (wipeArea > 0 && totalArea > 0) { int percent = (int) (wipeArea * 100 / totalArea); LogGloble.e(percent, percent + ); if (percent > 60) {mComplete = true;if (mOnTextClearListener != null) { mOnTextClearListener.onStratchCompleteListenner(mTextClear);}postInvalidate(); } }} } };}
篇4:Android View measure (二) 自定义UI控件measure相关
如果在onMeasure中使用return,并未进行setMeasuredDimension(width, height);类似操作,会出现以下异常
java.lang.IllegalStateException: onMeasure() did not set the measured dimension by calling setMeasuredDimension()
-待填充-
ViewGroup提供的measure方法measureChildren() - 该函数内使用for()循环调用measureChild()对每一个子视图进行measure操作 measureChild() - 为每一个子视图进行measure操作 measureChildWidthMargins() - 该函数与measureChild的唯一区别在于,measure时考虑把margin及padding也作为子视图大小的一部分
篇5:Android View measure (二) 自定义UI控件measure相关
从名字上可以看出这两个方法都是为了获取宽度的,相应的也有获取高度的方法,但是问题在于两者的区别是什么? 下面直接从源码中查看这些值是从哪里来的,从而获知两者的区别。
首先看下View.java中的getMeasuredWidth方法,源码如下:
public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Callback, AccessibilityEventSource { /** * Like {@link #getMeasuredWidthAndState}, but only returns the * raw width component (that is the result is masked by * {@link #MEASURED_SIZE_MASK}). * * @return The raw measured width of this view. */ public final int getMeasuredWidth() { // 直接返回mMeasuredWidth与后者相与清理掉其他开关获取真是measure大小 return mMeasuredWidth & MEASURED_SIZE_MASK; } /** *
This mehod must be called by {@link #onMeasure(int, int)} to store the * measured width and measured height. Failing to do so will trigger an * exception at measurement time.
* * @param measuredWidth The measured width of this view. May be a complex * bit mask as defined by {@link #MEASURED_SIZE_MASK} and * {@link #MEASURED_STATE_TOO_SMALL}. * @param measuredHeight The measured height of this view. May be a complex * bit mask as defined by {@link #MEASURED_SIZE_MASK} and * {@link #MEASURED_STATE_TOO_SMALL}. */ protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { // 通常在onMeasure中调用,传入测量过的视图宽度与高度 mMeasuredWidth = measuredWidth; mMeasuredHeight = measuredHeight; mPrivateFlags |= MEASURED_DIMENSION_SET; } /** * Bits of {@link #getMeasuredWidthAndState()} and * {@link #getMeasuredWidthAndState()} that provide the actual measured size. */ // MeasureSpec中的Mode或占用int类型中前几位 public static final int MEASURED_SIZE_MASK = 0x00ffffff; }从上面两个方法可以看出getMeasuredWidth的值是从mMeasuredWidth变量获取,而这个变量仅在View.setMeasuredDimension方法中继续初始化,从setMeasuredDimension方法的注释中就可以看出这个方式是在onMeasure中被调用,也就是getMeasuredWidth获取到的是在视图onMeasure方法中已经获取到视图的大小之后,才能进行赋值,
getMeasuredWidth获取的是通过onMeasure测量后的值,在onMeasure执行之前可以调用但是获取到的都是0(int类型的默认初始化值)。
上面已经知道getMeasuredWidth值的含义,接着来看下View.getWidth方法的源码:
public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Callback, AccessibilityEventSource {/** * Return the width of the your view. * * @return The width of your view, in pixels. */ @ViewDebug.ExportedProperty(category = “layout”) public final int getWidth() { // 视图的右侧减去左侧的值 return mRight - mLeft; } /** * Assign a size and position to this view. * * This is called from layout. * * @param left Left position, relative to parent * @param top Top position, relative to parent * @param right Right position, relative to parent * @param bottom Bottom position, relative to parent * @return true if the new size and position are different than the * previous ones * {@hide} */ protected boolean setFrame(int left, int top, int right, int bottom) { boolean changed = false; ...... // 四个值中任意一个发生改变就行 if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {changed = true;......mLeft = left;mTop = top;mRight = right;mBottom = bottom;...... } return changed; } public void layout(int l, int t, int r, int b) { ...... // 当前视图布局时执行,传入当前视图的上下左右边界值 boolean changed = setFrame(l, t, r, b); if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {......// 上面执行完成后才会触发onLayoutonLayout(changed, l, t, r, b); ...... } ...... mPrivateFlags &= ~FORCE_LAYOUT; }}
从上面的代码可以看出当视图layout操作时,会先调用setFrame方法传入left, top, right, bottom 这些值会在以后layout布局分析是进行详细解释,这些值是其父视图给他当前视图设定的可显示位置与大小(right - left 与 top - bottom获得)。 getWidth方法获取的宽度是当前视图可以在屏幕实际上占据的大小。
简单总结下,getMeasuredWidth是视图onMeasure指定的宽度(可以笼统的理解为视图内容区域的大小,虽然不严谨但是系统提供的布局控件都是这样,仅在自定义视图中因为覆写onMeasure可以忽略layout_width,layout_heigh随意指定其宽高),而getWidth是视图父视图指定当前视图可以在屏幕上显示的区域。
上一篇www.2cto.com/kf/03/382697.html
篇6:Android开发之自定义View开发及实例详解
在开发Android应用的过程中,难免需要自定义View,其实自定义View不难,只要了解原理,实现起来就没有那么难,
其主要原理就是继承View,重写构造方法、onDraw,(onMeasure)等函数。我自定义了个虚拟按键的View,
首先得自己写个自定义View类,这里我写了个VirtualKeyView类,继承自View类,实现了构造方法以及onDraw方法,以及实现了键盘按键的接口事件,实现代码如下:
复制代码
package com.czm.customview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
public class VirtualKeyView extends View{
private static final boolean bDebug = true;
private static final String TAG = VirtualKeyView.class.getSimpleName();
private Context mContext;
private Bitmap bmpRound;
private Bitmap bmpRound_press;
private Bitmap bmpOk;
private Bitmap bmpOk_press;
private int mWidth;//真实宽度
private int widthBg = 584;
//private int widthItemBg = 292;
private int widthMid = 220;//中心宽度
private int cir_Centre_X = 292;//圆心位置
private int cir_Centre_Y = 292;//圆心位置
private int bigRadius = 292;
private int smallRadius = 110;
private int smallCir_X = 182;
private int smallCir_Y = 182;
private float scale = 1.0f;
private boolean isInit = false;
private int inputPress = -1;//显示点击了哪个键
public VirtualKeyView(Context context) {
super(context);
// TODO Auto-generated constructor stub
mContext = context;
}
public VirtualKeyView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
mContext = context;
}
private void initData() {
mWidth = getWidth();
scale = mWidth*1.0f/widthBg;
cir_Centre_X = (int) (cir_Centre_X*scale);
cir_Centre_Y = (int) (cir_Centre_Y*scale);
bigRadius = (int) (bigRadius*scale);
smallRadius = (int) (smallRadius*scale);
smallCir_X = (int) (smallCir_X*scale);
smallCir_Y = (int) (smallCir_Y*scale);
initView();
isInit = true;
}
private void initView() {
if(mWidth == widthBg){
bmpRound = Utils.decodeCustomRes(mContext, R.drawable.controller_round);
bmpRound_press= Utils.decodeCustomRes(mContext, R.drawable.controller_round_bg_pressed);
bmpOk= Utils.decodeCustomRes(mContext, R.drawable.controller_ok);
bmpOk_press= Utils.decodeCustomRes(mContext, R.drawable.controller_ok_pressed);
} else {
int mid = (int) (widthMid*scale);
Bitmap bitmapTmp = Utils.decodeCustomRes(mContext, R.drawable.controller_round);
bmpRound = Bitmap.createScaledBitmap(bitmapTmp, mWidth, mWidth, true);
bitmapTmp.recycle();
bitmapTmp = null;
bitmapTmp = Utils.decodeCustomRes(mContext, R.drawable.controller_round_bg_pressed);
bmpRound_press = Bitmap.createScaledBitmap(bitmapTmp, mWidth, mWidth, true);
bitmapTmp.recycle();
bitmapTmp = null;
bitmapTmp = Utils.decodeCustomRes(mContext, R.drawable.controller_ok);







