一篇学会自定义View

原创 2016年05月30日 20:57:04

这里仅仅只对直接继承View来说明。下面是一个模仿QQ运动的一个View来讲解;

图示:


自定义View无外呼几个具体的步骤;

A.构造函数中初始化,一般为定义画笔,如果我们还为该view自定义了属性的话就要解析xml文件中的属性了,但是我觉得可以在代码中实现就没必要那么麻烦了。

B.onMeasure中测量View大小啊,关键调用setMeasuredDimension(adjust_width, adjust_height);宽和高就是我们要定义的大小了。我们一般按最小的一个为准,然后按固定比例缩放就可以了。

C.在OnSizeChanged()中根据我们获得的实际控件的宽和高,然后依照我们已经知晓的各种比例,这里包括各种字体的比例,图片比例,矩形,圆形,弧形,字体位置在这里都已可以计算出来的。就是简单的加减乘除了,复杂点搞个sin,cos或者搞个2介3介贝塞尔曲线等等了。

D.重点就在OnDraw()中了,其实如果在C步骤中我们定义好了而且计算精确的话,下面无非就是些调用函数的问题了。是一些死的东西。比如

画文字的话,要注意字体大小(根据字体高度适配),字体颜色(随便你了,只要不是色盲),字体的位置(根据我提供的工具就可以计算baseLineY)

画图片,就容易了,调用就行了,传个RectF,限制图片有多大,具体位置如何。

画弧形,还是要计算RectF,这个矩形是外接于弧形的,确定下弧形的圆形,弧的半径,就可以轻松得到这个RectF了。然后确定下弧的宽度。

画线,也容易定义起始点就ok了,画笔的描边宽度,填充方式,画笔颜色。画笔的线冒(就是画完线后可以看到两端有圆润,有 方的,或者什么都没有)

复杂点的线状,就是定义Path,然后moveto来定义path的起点,然后lineto一个个点连起来。在path中还可以添加弧的,想要闭合了就close,想要为path设置虚线效果,

生锈铁丝效果等用setPathEffect().

要动画的话,可以使用属性动画ValueAnimator,监听值的变化来改变onDraw中我们要变化的值,然后调用nvalidate()不断刷新即可。

想要响应点击事件就onTouchEvent中识别下手势。比如单击,双击,长按,拖拉等等。这就涉及到手势这块了。这里我们就简单的响应单击事件,单击“查看”那个区域响应单击事件。这里我们定义了一个小方块clcikRectF如果单击是发生在这里边,就使用定义一个接口给回调出去了。

E.定义好了View之后,可以在Xml文件中使用,或者在java中new都行。


以上步骤在该View中都有涉及;


1.定义一个健康参数的实体类;Sports 

package com.example.sport;


import java.util.Arrays;
import java.util.List;


import android.R.integer;


public class Sports {
public int[] recentDaysSteps = new int[7];  
public int friendsAverageSteps;
public int selfRank;
public List<String> arrayString;
public String championName;


public Sports(int[] recentDaysSteps, int friendsAverageSteps, int selfRank,
List<String> arrayString, String championName) {
super();
this.recentDaysSteps = recentDaysSteps;
this.friendsAverageSteps = friendsAverageSteps;
this.selfRank = selfRank;
this.arrayString = arrayString;
this.championName = championName;
}


public Sports() {
super();
}


public float getRatio() {
return (float) (1.0*recentDaysSteps[6] / friendsAverageSteps);
}


public float getAngle() {

float swipe = getRatio() * 300;

swipe = swipe>=300?300:swipe;
return swipe  ;
}

public int getAverageRecentSteps(){
float average =0;
int length = recentDaysSteps.length;
for(int i=0;i<length;i++){
average += recentDaysSteps[i];
}
return (int) (average/length);
}

public int getMaxFromRecentDaysSteps(){
int length = recentDaysSteps.length;
int max = recentDaysSteps[0];
for(int i=1;i<length;i++){
if(recentDaysSteps[i]>max){
max = recentDaysSteps[i];
}
}
return max;
}
}


2.这里写一个有关画字体时,计算位置的工具类:DrawTextUtil 

package com.example.sport;


import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.graphics.Rect;


public class DrawTextUtil {
/**
* mPaint.setTextAlign(Align.CENTER); canvas.drawText("aafdADF", x, y,
* mPaint); Align.CENTER, 绘制的时候保证整体文字的中间在(x,y)处 Align.LEFT,
* 绘制的时候保证整体文字最左边位于(x,y)处 Align.Right, 绘制的时候保证整体文字最右边位于(x,y)处


* FontMetrics fontMetrics = mPaint.getFontMetrics(); fontMetrics.ascent;
* 可绘制的最顶部 相当于影片一样,显示的安全区域 fontMetrics.descent; 可绘制的最底部 fontMetrics.top;
* 物理最顶部,相当于电视剧的屏幕一样 fontMetrics.bottom; 物理最底部

* 我们的文字是显示在ascent与descent之间的 这4个值都是相当于baseline而言的。所以fontMetrics.ascent=
* ascent线-baseline线 得到的结果是负的

* 从而可以推出; ascent线Y坐标 = baseline线的y坐标 + fontMetric.ascent; descent线Y坐标 =
* baseline线的y坐标 + fontMetric.descent; top线Y坐标 = baseline线的y坐标 +
* fontMetric.top; bottom线Y坐标 = baseline线的y坐标 + fontMetric.bottom;


* 所绘文字宽度、int width = paint.measureText(String text); 高度
* Paint.FontMetricsInt fm = paint.getFontMetricsInt(); int top = baseLineY
* + fm.top; int bottom = baseLineY + fm.bottom; int height = fm.bottom -
* fm.top; 和最小矩形获取
*/


/**

* @param textPaint
* @return 
*         fontMetrics.top,fontMetrics.ascent,fontMetrics.descent,fontMetrics
*         .bottom
*/
private static float[] getMuxLineOffset(Paint textPaint) {
FontMetrics fontMetrics = textPaint.getFontMetrics();
return new float[] { fontMetrics.top, fontMetrics.ascent,
fontMetrics.descent, fontMetrics.bottom };
}


public static float getTextWidth(Paint textPaint, String text) {
return textPaint.measureText(text);
}


// 物理尺寸的高度,实际中就是我们用来确定基线的
public static float getTextMaxHeight(Paint textPaint) {
Paint.FontMetrics fm = textPaint.getFontMetrics();
return fm.bottom - fm.top;
}


// 显示区域刚好包裹文字的高度
public static int getTextMinHeight(Paint textPaint, String text) {
Rect minRect = new Rect();
textPaint.getTextBounds(text, 0, text.length(), minRect);
return minRect.bottom - minRect.top;
}


/**
* 返回基线位置,已知top点Y坐标

* @param textPaint
* @return
*/
public static float getBaseLineYFromTopY(Paint textPaint, float textTopY) {
float baseLineY = textTopY - getMuxLineOffset(textPaint)[0];
return baseLineY;
}


/**
* 返回基线位置,已知center点Y坐标
* @param textPaint
* @param textCenterY
* @return
*/
public static float getBaseLineYFromCenterY(Paint textPaint,
float textCenterY) {
float baseLineY = textCenterY + getTextMaxHeight(textPaint) / 2
- getMuxLineOffset(textPaint)[3];
return baseLineY;
}
}


3.开始绘制QQ运动View:

package com.example.sport;


import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.RectF;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;


//模仿QQ运动控件
public class SportView extends View {
public interface OnLookChanmpionInfo {
public void lookChanmpionInfo(String chanmpionName);
}

//下边都是些根据具体的UI用尺子量出的比例。
private static final float LARGE_CIRCLE_LEFT_W = (float) (3 / 14.8);
private static final float LARGE_CIRCLE_RADIUS_W = (float) (((14.8 - 3 * 2) / 2) / 14.8);
private static final float LARGE_CIRCLE_TOP_H = (float) (1.2 / 18.3);
private static final float LARGE_CIRCLE_WIDTH_H = (float) (0.5 / 18.3);
private static final float RADIO_BOTTOM_H_ = (float) (2.8 / 18.3);
private static final float RADIO_H_W = (float) (18.3 / 14.8);


private static final float RECT_ROUND_RADIUS_H = (float) (0.3 / 18.3);
private float bottomHeight;
private Path bottomPath;
private RectF clickRect;
private int defaultAverageLineColor = Color.parseColor("#DDDDDD");
private int defaultBackgroundColor = Color.WHITE;
private int defaultBottomBackgroundColor = Color.parseColor("#2EC3FF");
private int defaultLargeCircleBackgroundColor = Color.parseColor("#CCEDF9");


private int defaultLargeCircleColor = Color.parseColor("#26B8F3");
private int defaultMaxLargeTextColor = Color.parseColor("#26B8F3");
private int defaultMinTextColor = Color.parseColor("#AEAFB2");
private int defaultSportValuesColor_WhenLowAverage = Color
.parseColor("#DDDDDD");
private int defaultSportValuesColor_WhenOverAverage = Color
.parseColor("#26B8F3");
private float dividerWidth;


private float firstValueX;
private boolean hasDown;
private Bitmap iconBitmap;
private int iconWidth;
private float largeCircleRadius;


private RectF largeCircleRect;
private float largeCircleWidth;
private float lookTextX;
private float maxValueY;
private Paint mPaint;
private Bitmap nextBitmap;
private float nextIcon_left;
private int nextIconSize;
private OnLookChanmpionInfo onLookChanmpionInfo;
private float perValueWidth;
private RectF rect;
private float rect_round_radius;
private Sports sports;
private float tempSwipeAngle;
private TextPaint textPaint;
private float valueBottomY;


private float viewHeight;


private float viewWidth;
private float textSize_one;
private float textSize_two;
private float textSize_three;
private float textSize_four;

//构造方法
public SportView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setAntiAlias(true);
textPaint = new TextPaint();
textPaint.setAntiAlias(true);
iconBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
nextBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.next);
}

//根据measureSpec计算大小
private int adjustViewSize(int measureSpec) {
int defaultSize = Integer.MAX_VALUE;
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);
if (MeasureSpec.AT_MOST == mode || MeasureSpec.EXACTLY == mode) {
defaultSize = size;
}
return defaultSize;
}


private void drawBottom(Canvas canvas) {
// 画冠军icon
float icon_left = (float) (viewWidth * 1.1 / 14.8);
float icon_top = (float) (viewHeight - 1.8 / 2.5 * bottomHeight);
canvas.drawBitmap(iconBitmap, null, new RectF(icon_left, icon_top,
icon_left + iconWidth, icon_top + iconWidth), null);


textPaint.setTextAlign(Align.LEFT);
textPaint.setColor(Color.WHITE);
float x = (float) (viewWidth * 2.6 / 14.8);
float y = DrawTextUtil.getBaseLineYFromCenterY(textPaint, viewHeight
- bottomHeight / 2);
textPaint.setTextSize(textSize_two);
canvas.drawText(sports.championName + "获得今日冠军", x, y, textPaint);


textPaint.setTextSize(textSize_two);
canvas.drawText("查看", lookTextX, y, textPaint);


// 画查看的箭头
canvas.drawBitmap(nextBitmap, null, new RectF(nextIcon_left, viewHeight
- bottomHeight / 2 - nextIconSize / 2, nextIcon_left
+ nextIconSize, viewHeight - bottomHeight / 2 + nextIconSize
/ 2), null);
}


private void drawLargeCircleForSports(Canvas canvas) {
mPaint.setPathEffect(null);
// 大圆弧背景
mPaint.setColor(defaultLargeCircleBackgroundColor);
mPaint.setStyle(Style.STROKE);
mPaint.setStrokeCap(Cap.ROUND);
mPaint.setStrokeWidth(largeCircleWidth);
canvas.drawArc(largeCircleRect, 120f, 300, false, mPaint);


// 大圆弧前景
mPaint.setColor(defaultLargeCircleColor);
canvas.drawArc(largeCircleRect, 120f, tempSwipeAngle, false, mPaint);


// 大圆弧内文字
textPaint.setTextSize(textSize_two);
textPaint.setTextAlign(Align.CENTER);
textPaint.setColor(defaultMinTextColor);
float centerX = viewWidth / 2;


float textTopY1 = (float) (viewHeight * (3.8 / 18.3));
float y1 = DrawTextUtil.getBaseLineYFromTopY(textPaint, textTopY1);
canvas.drawText("截至23:59已走", centerX, y1, textPaint);


textPaint.setColor(defaultMaxLargeTextColor);
textPaint.setTextSize(textSize_four);
float textTopY2 = (float) (viewHeight * (4.8 / 18.3));
float y2 = DrawTextUtil.getBaseLineYFromTopY(textPaint, textTopY2);
canvas.drawText(sports.recentDaysSteps[6] + "", centerX, y2, textPaint);


textPaint.setColor(defaultMinTextColor);
textPaint.setTextSize(textSize_two);
float textTopY3 = (float) (viewHeight * (7.1 / 18.3));
float y3 = DrawTextUtil.getBaseLineYFromTopY(textPaint, textTopY3);
canvas.drawText("好友平均 " + sports.friendsAverageSteps + " 步", centerX,
y3, textPaint);


float textCenterY4 = (float) (viewHeight * (9.8 / 18.3));


textPaint.setTextSize(textSize_two);
textPaint.setColor(defaultMinTextColor);
canvas.drawText("第", (float) (centerX - 1.2 / 14.8 * viewWidth),
DrawTextUtil.getBaseLineYFromCenterY(textPaint, textCenterY4),
textPaint);


textPaint.setTextSize(textSize_three);
textPaint.setColor(defaultMaxLargeTextColor);
canvas.drawText(sports.selfRank + "", centerX,
DrawTextUtil.getBaseLineYFromCenterY(textPaint, textCenterY4),
textPaint);


textPaint.setTextSize(textSize_two);
textPaint.setColor(defaultMinTextColor);
canvas.drawText("名", (float) (centerX + 1.2 / 14.8 * viewWidth),
DrawTextUtil.getBaseLineYFromCenterY(textPaint, textCenterY4),
textPaint);


}


private void drawOuterAndInnerRect(Canvas canvas) {
mPaint.setColor(defaultBackgroundColor);
mPaint.setStyle(Style.FILL);
canvas.drawRoundRect(rect, rect_round_radius, rect_round_radius, mPaint);


mPaint.setColor(defaultBottomBackgroundColor);
mPaint.setStyle(Style.FILL);
canvas.drawPath(bottomPath, mPaint);
}


private void drawRecentDaysRecords(Canvas canvas) {
textPaint.setTextAlign(Align.LEFT);
textPaint.setColor(defaultMinTextColor);
textPaint.setTextSize(textSize_one);
float textLeft = (float) (viewWidth * 0.8 / 14.8);
canvas.drawText("最近7天", textLeft, DrawTextUtil.getBaseLineYFromTopY(
textPaint, (float) (viewHeight * 11.3 / 18.3)), textPaint);


textPaint.setTextAlign(Align.RIGHT);
canvas.drawText("平均" + sports.getAverageRecentSteps() + "步/天",
viewWidth - textLeft, DrawTextUtil.getBaseLineYFromTopY(
textPaint, (float) (viewHeight * 11.3 / 18.3)),
textPaint);


// //记录虚线Y坐标
float valueDashLineY = (float) (viewHeight * 12.8 / 18.3);
float dashWidth = (viewWidth - 2 * textLeft) / 50;
float[] intervals = { dashWidth, dashWidth };


// 画虚线
mPaint.setStrokeWidth(3);
mPaint.setColor(defaultAverageLineColor);
mPaint.setPathEffect(new DashPathEffect(intervals, 0));
Path dashPath = new Path();
dashPath.moveTo(textLeft + dashWidth / 2, valueDashLineY);
dashPath.lineTo(viewWidth - textLeft, valueDashLineY);
canvas.drawPath(dashPath, mPaint);


// 画7天柱形
int maxSteps = sports.getMaxFromRecentDaysSteps();
float pxPerStep = (valueBottomY - maxValueY) / maxSteps;
mPaint.setStrokeWidth(perValueWidth);
for (int i = 0; i < sports.recentDaysSteps.length; i++) {
float stopY = valueBottomY - pxPerStep * sports.recentDaysSteps[i];
if (stopY < valueDashLineY + 1.5) {
mPaint.setColor(defaultSportValuesColor_WhenOverAverage);
} else {
mPaint.setColor(defaultSportValuesColor_WhenLowAverage);
}
canvas.drawLine(firstValueX + dividerWidth * i, valueBottomY,
firstValueX + dividerWidth * i, stopY, mPaint);
}


// 画7天下的文字
textPaint.setTextAlign(Align.CENTER);
textPaint.setTextSize(textSize_one);
float textTopY = (float) (viewHeight * 14 / 18.3);
for (int i = 0; i < sports.recentDaysSteps.length; i++) {
canvas.drawText(sports.arrayString.get(i), firstValueX
+ dividerWidth * i,
DrawTextUtil.getBaseLineYFromTopY(textPaint, textTopY),
textPaint);
}


}


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


drawOuterAndInnerRect(canvas);


drawLargeCircleForSports(canvas);


drawRecentDaysRecords(canvas);


drawBottom(canvas);
}


// 测量View宽和高,并根据宽高比调整
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int adjust_width = adjustViewSize(widthMeasureSpec);
int adjust_height = adjustViewSize(heightMeasureSpec);


// 以用户填写的宽和高的最小值作为基准,缩放得到实际宽和高
if (adjust_height >= adjust_width) {
adjust_height = (int) (adjust_width * RADIO_H_W);
} else {
adjust_width = (int) (adjust_height / RADIO_H_W);
}


setMeasuredDimension(adjust_width, adjust_height);
}


@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
viewWidth = w;
viewHeight = h;


// 圆角矩形半径
rect_round_radius = (RECT_ROUND_RADIUS_H * viewHeight);


// 最外面的圆角矩形
rect = new RectF(0, 0, viewWidth, viewHeight);


// 最下面的圆角矩形的高度
bottomHeight = RADIO_BOTTOM_H_ * viewHeight;


// 最下面的圆角矩形
bottomPath = new Path();
bottomPath.moveTo(0, viewHeight - bottomHeight);
RectF bottomRect = new RectF(0, viewHeight - bottomHeight, viewWidth,
viewHeight);
float[] radii = new float[] { 0, 0, 0, 0, rect_round_radius,
rect_round_radius, rect_round_radius, rect_round_radius };
bottomPath.addRoundRect(bottomRect, radii, Direction.CW);


// 大圆环的宽度
largeCircleWidth = viewHeight * LARGE_CIRCLE_WIDTH_H;
// 大圆弧半径
largeCircleRadius = viewWidth * LARGE_CIRCLE_RADIUS_W;


// 大圆弧矩形
largeCircleRect = new RectF(LARGE_CIRCLE_LEFT_W * viewWidth,
LARGE_CIRCLE_TOP_H * viewHeight, LARGE_CIRCLE_LEFT_W
* viewWidth + 2 * largeCircleRadius, LARGE_CIRCLE_TOP_H
* viewHeight + 2 * largeCircleRadius);


firstValueX = (float) (viewWidth * 1.8 / 14.8);
valueBottomY = (float) (viewHeight * 13.6 / 18.3);
maxValueY = (float) (viewHeight * 12 / 18.3);
perValueWidth = (float) (viewWidth * 0.4 / 14.8);
dividerWidth = (float) (viewWidth * 1.8 / 14.8);


iconWidth = (int) (bottomHeight * 1.1 / 2.5);
nextIconSize = iconWidth / 2;
nextIcon_left = (float) (viewWidth * 13.6 / 14.8);
lookTextX = (float) (viewWidth * 12.3 / 14.8);


float top = viewHeight - bottomHeight / 2 - nextIconSize / 2;
clickRect = new RectF(lookTextX - nextIconSize, viewHeight
- bottomHeight, viewWidth, viewHeight);


//适配字体
textSize_one = (float) (viewHeight * 0.3 / 18.3);
textSize_two = (float) (viewHeight * 0.4 / 18.3);
textSize_three = (float) (viewHeight * 0.5 / 18.3);
textSize_four = (float) (viewHeight * 1.1 / 18.3);


}


@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
float x2 = event.getX();
float y2 = event.getY();
if (clickRect.contains(x2, y2)) {
if (action == MotionEvent.ACTION_DOWN) {
hasDown = true;
return true;
} else if (action == MotionEvent.ACTION_UP && hasDown == true) {
// 单击
hasDown = false;
if (onLookChanmpionInfo != null) {
onLookChanmpionInfo.lookChanmpionInfo(sports.championName);
}
}
}
return super.onTouchEvent(event);
}


public void setIconBitmap(Bitmap iconBitmap) {
this.iconBitmap = iconBitmap;
}


public void setOnLookChanmpionInfo(OnLookChanmpionInfo onLookChanmpionInfo) {
this.onLookChanmpionInfo = onLookChanmpionInfo;
}


public void setSports(Sports sports) {
this.sports = sports;
ValueAnimator animator = ValueAnimator.ofFloat(0f, sports.getAngle());
animator.setDuration(2000);
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
tempSwipeAngle = (float) animation.getAnimatedValue();
invalidate();
}
});
animator.start();
}
}


4.使用

package com.example.sport;


import java.util.ArrayList;
import java.util.List;


import android.app.Activity;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.widget.Toast;


import com.example.sport.SportView.OnLookChanmpionInfo;


public class MainActivity extends Activity implements OnLookChanmpionInfo {


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);


SportView sportView = (SportView) findViewById(R.id.sportView);


Sports sports = createSports();
sportView.setSports(sports);
sportView.setIconBitmap(BitmapFactory.decodeResource(getResources(),R.drawable.icon_i));
sportView.setOnLookChanmpionInfo(this);
}


private Sports createSports() {
Sports sports = new Sports();
sports.recentDaysSteps[0] = 230;
sports.recentDaysSteps[1] = 600;
sports.recentDaysSteps[2] = 1002;
sports.recentDaysSteps[3] = 840;
sports.recentDaysSteps[4] = 2014;
sports.recentDaysSteps[5] = 365;
sports.recentDaysSteps[6] = 740;
sports.friendsAverageSteps = 1500;
sports.selfRank = 22;
List<String> list = new ArrayList<>();
for (int i = 23; i < 30; i++) {
list.add(i + "日");
}
sports.arrayString = list;
sports.championName = "安妮";
return sports;
}


@Override
public void lookChanmpionInfo(String chanmpionName) {
Toast.makeText(this,chanmpionName, 0).show();
}
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Android中自定义View中的Paint,Canvas,RectF,Path

每一个View的绘制过程都必须经历三个最主要的过程,也就是onMeasure()、onLayout() 和 onDraw() 一般在构造方法中做这样的事情: public RoundView(C...
  • dodan
  • dodan
  • 2016年07月05日 23:53
  • 1688

自定义View,有这一篇就够了

我的简书同步发布:自定义View,有这一篇就够了 为了扫除学习中的盲点,尽可能多的覆盖Android知识的边边角角,决定对自定义View做一个稍微全面一点的使用方法总结,在内容并没有什么独特,其他大神...

一步一步教你学会自定义View并了解其绘制原理(一)

自定义view的绘制过程,看了很多关于自定义view的帖子、博客,写的也都不错,但我想通过图片+代码+注释的方式写一篇因为图片带给人的记忆效果远比长篇大论的文字好的多,再加上代码的辅助说明,那就更容易...

自定义View

  • 2017年11月19日 19:42
  • 392B
  • 下载

自定义view(音量条)

  • 2017年11月09日 15:14
  • 19.83MB
  • 下载

2014最后一篇,记ExpandableListViewd的自定义

2014最后一篇,记ExpandableListViewd的自定义,整个流程下来可能二级项会出现不能点击的情况,这时候需要将二级项的布局文件里的所有Layout里增加属性,我这里只是增加了根layou...

Android自定义View

  • 2016年01月05日 17:54
  • 1.26MB
  • 下载

转载一篇关于HTML5 data-* 自定义属性的文章

HTML5 data-* 自定义属性 在jQuery的attr与prop提到过在IE9之前版本中如果使用property不当会造成内存泄露问题,而且关于Attribute和Property的区别也让...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:一篇学会自定义View
举报原因:
原因补充:

(最多只允许输入30个字)