android自定义view--色彩选择器
2016-05-26 19:26
453 查看
先上图,大概就是这样的一个效果:
可以选用progressbar做,在这里我直接画了两个圆角矩形+圆
下面贴代码:
一块一块说明一下:
首先,构造函数:
里面使用了一些自定义属性,自定义属性是在res/values/attrs.xml里面注册的:
然后在布局文件里给其赋值即可,注意使用时要声明包,比如声明了
,那么用时只需
最后,用完TypedArray别忘了回收(recycle)
其次,onMeasure
onMeasure方法即测量控件的大小,系统测量完布局中的大小后会以widthMeasureSpec和heightMeasureSpec作为参数传递进来,其是一个32位的int值,其中最高2位表示mode,低30位表示size。而mode又分为3类:
UNSPECIFIED(未指定),父元素不对子元素施加任何束缚,子元素可以得到任意想要的大小,我们平时一般不用;
EXACTLY(完全),父元素决定子元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小,当我们设置match_parent时就是这个mode;
AT_MOST(至多),子元素至多达到指定大小的值,我们设置wrap_content或者具体的值时就是这个mode。
根据得到的值,我们就可以设置这个控件的位置了,这里我设置这个控件为竖直居中,水平依赖于布局中设置的padding值。
measure完后有init()方法,初始化一些参数的值是在这个方法里进行的。其中重要的是色彩块的初始化,我们将其和ondraw一起说明:
两个圆角矩形是用不同的方法绘制的,
第一个是用颜色渲染做的:
在init中用
设置矩形渲染,在ondraw中用
第二个是用画bitmap染像素点做的:
在init中用
创建这个bitmap块,在ondraw中首先要填充这个bitmap块的像素点,写了一个calculateRectBitmap
HSV 分别为色相,饱和度,明度,色相H就是我们第一个圆角矩形中选中的颜色,它的初始化是在init里进行的
接下来在ondraw里画这个圆角矩形:
因为是圆角,所以我们使用了图形遮盖的方法来实现圆角bitmap,其要点就是设置画笔为PorterDuff.Mode.SRC_IN,别忘了保存canvas的状态。
接下来就是滑动选颜色了,在ontouchevent里面写:
当按下时,判断是否按在了圆形滑块处,如果是就可以拖动,如果不是就不能,滑动时,可以变颜色。这里我们设置了监听事件,可以在activity里调用,比如说我们在activity里有一个view叫colorchangeview,然后我们使用了这个colorpickerview,就可以这么写:
这样我们拖动的同时,colorchangeview就可以变颜色了。
最后,说明一下变颜色的设置:
主要有这么几个方法:
滑动第一个圆角矩形时,使用calculateColor()方法由于其是线性渲染出来的,所以可以根据其坐标来计算颜色的argb值,argb即透明度,红色,绿色,蓝色,对应2位16进制数,比如说0xFFFF0000,其a为ff,r为ff,g为00,b为00,我们看到的就是红色。color和其都有对应的方法,见上面,比如Color.green()就是获取color的green值,计算各颜色的比重后再用Color.argb(a, r, g, b)把数字转为颜色。
滑动第二个圆角矩形时,使用calculateHSVColor()方法,就是直接使用hsv的转换,上文已经说过,我们只计算其v即明度的值,根据坐标计算再转换即可,使用,Color.HSVToColor(hsv)就是把一个hsv数组转化为颜色。
可以选用progressbar做,在这里我直接画了两个圆角矩形+圆
下面贴代码:
public class ColorPickerView extends View { private static final int[] COLORS = new int[]{0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00, 0xFFFFFF00, 0xFFFF0000}; private int radius; private int fingerflag; private int rectBeginx; private int rectEndx; private int firstFingerx; private int firstRectBeginy; private int firstRectEndy; private int secondFingerx; private int secondRectBeginy; private int secondRectEndy; private int resultColor; private int rectHeight; private int rectCorner; private int firstRectCentre; private int secondRectCentre; private int rectLength; private int rectDistance; private int rectSetLength; private int lastfirstposition; private int lastsecondposition; private Paint rectPaint; private Paint circlePaint; private Paint blankPaint; private Paint mRectBitmapPaint; private RectF firstRect; private RectF secondRect; private Shader shader; private Bitmap mRectBitmap; private OnColorChangedListener onColorChangedListener; private Context context; private int windowheight; private int windowwidth; private float[] mHSV = new float[3]; private boolean canMove; public ColorPickerView(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ColorPickerView); rectHeight = (int) a.getDimension(R.styleable.ColorPickerView_rectHeight, 8); radius = (int) a.getDimension(R.styleable.ColorPickerView_radius, 24); ifClickChange = a.getBoolean(R.styleable.ColorPickerView_ifClickChange, false); rectDistance= (int) a.getDimension(R.styleable.ColorPickerView_rectDistance,3*radius); rectSetLength= (int) a.getDimension(R.styleable.ColorPickerView_rectLength,0); a.recycle(); } public void setColor(int color) { resultColor = color; } public void setBaseColor(int color) { setColor(calculateHSVColor(secondFingerx)); Color.colorToHSV(color, mHSV); } private void calculateRectBitmap() { for (int x = 0; x < mRectBitmap.getWidth(); x++) { for (int y = 0; y < mRectBitmap.getHeight(); y++) { float[] tmpHSV = new float[3]; tmpHSV[0] = mHSV[0]; tmpHSV[1] = 1; tmpHSV[2] = (float) x / mRectBitmap.getWidth(); mRectBitmap.setPixel(x, y, Color.HSVToColor(tmpHSV)); } } } private int calculateHSVColor(float x) { float[] hsv = new float[3]; hsv[0] = mHSV[0]; hsv[1] = 1; hsv[2] = (x - rectBeginx) / rectLength; return Color.HSVToColor(hsv); } private int calculateColor(float x) { int unitlength = rectLength / (COLORS.length - 1); int fingerlength = (int) (x - rectBeginx); int unit = fingerlength / unitlength; float percent = (float) fingerlength / unitlength - unit; int c0; int c1; if (unit == 6) { return COLORS[COLORS.length - 1]; } else { c0 = COLORS[unit]; c1 = COLORS[unit + 1]; } int a = ave(Color.alpha(c0), Color.alpha(c1), percent); int r = ave(Color.red(c0), Color.red(c1), percent); int g = ave(Color.green(c0), Color.green(c1), percent); int b = ave(Color.blue(c0), Color.blue(c1), percent); return Color.argb(a, r, g, b); } private int ave(int s, int d, float p) { return s + Math.round(p * (d - s)); } public void setOnColorChangedListener(OnColorChangedListener listener) { this.onColorChangedListener = listener; } public void init() { WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); windowheight = wm.getDefaultDisplay().getHeight(); windowwidth = wm.getDefaultDisplay().getWidth(); rectCorner = rectHeight; rectLength = rectEndx - rectBeginx; firstRectEndy = firstRectBeginy + rectHeight; firstFingerx = rectBeginx; firstRectCentre = (firstRectBeginy + firstRectEndy) / 2; secondRectEndy = secondRectBeginy + rectHeight; secondFingerx = rectEndx; secondRectCentre = (secondRectBeginy + secondRectEndy) / 2; if (lastfirstposition!=0){ firstFingerx=lastfirstposition; } if (lastsecondposition!=0){ secondFingerx=lastsecondposition; } rectPaint = new Paint(Paint.ANTI_ALIAS_FLAG); circlePaint = new Paint(); circlePaint.setColor(Color.WHITE); blankPaint = new Paint(Paint.ANTI_ALIAS_FLAG); blankPaint.setStyle(Paint.Style.STROKE); blankPaint.setColor(Color.GRAY); mRectBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); firstRect = new RectF(rectBeginx, firstRectBeginy, rectEndx, firstRectEndy); shader = new LinearGradient(rectBeginx, 0, rectEndx, 0, COLORS, null, Shader.TileMode.MIRROR); secondRect = new RectF(rectBeginx, secondRectBeginy, rectEndx, secondRectEndy); mRectBitmap = Bitmap.createBitmap(100, 1, Bitmap.Config.RGB_565); resultColor = calculateColor(firstFingerx); Color.colorToHSV(resultColor, mHSV); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); calculateRectBitmap(); rectPaint.setShader(shader); canvas.drawRoundRect(firstRect, rectCorner, rectCorner, rectPaint); canvas.drawCircle(firstFingerx, firstRectCentre, radius, circlePaint); canvas.drawCircle(firstFingerx, firstRectCentre, radius, blankPaint); int sc = canvas.saveLayer(0, 0, windowwidth, windowheight, null, Canvas.ALL_SAVE_FLAG); mRectBitmapPaint.reset(); canvas.drawRoundRect(secondRect, rectCorner, rectCorner, mRectBitmapPaint); mRectBitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(mRectBitmap, null, secondRect, mRectBitmapPaint); canvas.restoreToCount(sc); canvas.drawCircle(secondFingerx, secondRectCentre, radius, circlePaint); canvas.drawCircle(secondFingerx, secondRectCentre, radius, blankPaint); } @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (x > firstFingerx - 2 * radius && x < firstFingerx + 2 * radius && y < firstRectCentre + radius) { fingerflag = 1; canMove = true; } if (x > secondFingerx - 2 * radius && x < secondFingerx + 2 * radius && y > secondRectCentre - radius) { fingerflag = 2; canMove = true; } case MotionEvent.ACTION_MOVE: if (canMove){ if (fingerflag == 1) { if (x > rectBeginx && x < rectEndx) { firstFingerx = (int) x; } else if (x < rectBeginx) { firstFingerx = rectBeginx; } else if (x > rectEndx) { firstFingerx = rectEndx; } setBaseColor(calculateColor(firstFingerx)); if (onColorChangedListener != null) { onColorChangedListener.onColorChanged(resultColor); } invalidate(); break; } else if (fingerflag == 2) { if (x >= rectBeginx && x <= rectEndx) { secondFingerx = (int) x; } else if (x < rectBeginx) { secondFingerx = rectBeginx; } else if (x > rectEndx) { secondFingerx = rectEndx; } setColor(calculateHSVColor(secondFingerx)); if (onColorChangedListener != null) { onColorChangedListener.onColorChanged(resultColor); } invalidate(); break; } } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: canMove = false; break; } return true; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width; int height; if (widthMode == MeasureSpec.AT_MOST) { width = Math.min(574, widthSize); } else { width = widthSize; } if (heightMode == MeasureSpec.AT_MOST) { height = Math.min(188, heightSize); } else { height = heightSize; } rectBeginx = getPaddingLeft(); if (rectSetLength != 0) { rectEndx = rectBeginx + rectSetLength; } else { rectEndx = widthSize - getPaddingRight(); } firstRectBeginy = (heightSize - 5 * radius) / 2 + radius - rectHeight / 2 + getPaddingTop(); secondRectBeginy = firstRectBeginy + rectDistance - getPaddingBottom(); setMeasuredDimension(width, height); init(); } public void setPosition(int firstFingerx, int secondFingerx) { if (firstFingerx != 0 && secondFingerx != 0) { this.lastfirstposition = firstFingerx; this.lastsecondposition = secondFingerx; } invalidate(); } public int[] getPosition() { return new int[]{firstFingerx, secondFingerx}; } public interface OnColorChangedListener { void onColorChanged(int color); } }
一块一块说明一下:
首先,构造函数:
public ColorPickerView(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ColorPickerView); rectHeight = (int) a.getDimension(R.styleable.ColorPickerView_rectHeight, 8); radius = (int) a.getDimension(R.styleable.ColorPickerView_radius, 24); rectDistance= (int) a.getDimension(R.styleable.ColorPickerView_rectDistance,3*radius); rectSetLength= (int) a.getDimension(R.styleable.ColorPickerView_rectLength,0); a.recycle(); }
里面使用了一些自定义属性,自定义属性是在res/values/attrs.xml里面注册的:
<declare-styleable name="ColorPickerView"> <attr name="rectHeight" format="dimension"/> <attr name="rectLength" format="dimension"/> <attr name="radius" format="dimension"/> <attr name="rectDistance" format="dimension"/> </declare-styleable>
然后在布局文件里给其赋值即可,注意使用时要声明包,比如声明了
xmlns:app="http://schemas.android.com/apk/res-auto"
,那么用时只需
app:ArrowDirection="left" app:ArrowSize="16"即可
最后,用完TypedArray别忘了回收(recycle)
其次,onMeasure
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width; int height; if (widthMode == MeasureSpec.AT_MOST) { width = Math.min(574, widthSize); } else { width = widthSize; } if (heightMode == MeasureSpec.AT_MOST) { height = Math.min(188, heightSize); } else { height = heightSize; } rectBeginx = getPaddingLeft(); if (rectSetLength != 0) { rectEndx = rectBeginx + rectSetLength; } else { rectEndx = widthSize - getPaddingRight(); } firstRectBeginy = (heightSize - 5 * radius) / 2 + radius - rectHeight / 2 + getPaddingTop(); secondRectBeginy = firstRectBeginy + rectDistance - getPaddingBottom(); setMeasuredDimension(width, height); init(); }
onMeasure方法即测量控件的大小,系统测量完布局中的大小后会以widthMeasureSpec和heightMeasureSpec作为参数传递进来,其是一个32位的int值,其中最高2位表示mode,低30位表示size。而mode又分为3类:
UNSPECIFIED(未指定),父元素不对子元素施加任何束缚,子元素可以得到任意想要的大小,我们平时一般不用;
EXACTLY(完全),父元素决定子元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小,当我们设置match_parent时就是这个mode;
AT_MOST(至多),子元素至多达到指定大小的值,我们设置wrap_content或者具体的值时就是这个mode。
根据得到的值,我们就可以设置这个控件的位置了,这里我设置这个控件为竖直居中,水平依赖于布局中设置的padding值。
measure完后有init()方法,初始化一些参数的值是在这个方法里进行的。其中重要的是色彩块的初始化,我们将其和ondraw一起说明:
public void init() { WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); windowheight = wm.getDefaultDisplay().getHeight(); windowwidth = wm.getDefaultDisplay().getWidth(); rectCorner = rectHeight; rectLength = rectEndx - rectBeginx; firstRectEndy = firstRectBeginy + rectHeight; firstFingerx = rectBeginx; firstRectCentre = (firstRectBeginy + firstRectEndy) / 2; secondRectEndy = secondRectBeginy + rectHeight; secondFingerx = rectEndx; secondRectCentre = (secondRectBeginy + secondRectEndy) / 2; if (lastfirstposition!=0){ firstFingerx=lastfirstposition; } if (lastsecondposition!=0){ secondFingerx=lastsecondposition; } rectPaint = new Paint(Paint.ANTI_ALIAS_FLAG); circlePaint = new Paint(); circlePaint.setColor(Color.WHITE); blankPaint = new Paint(Paint.ANTI_ALIAS_FLAG); blankPaint.setStyle(Paint.Style.STROKE); blankPaint.setColor(Color.GRAY); mRectBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); firstRect = new RectF(rectBeginx, firstRectBeginy, rectEndx, firstRectEndy); shader = new LinearGradient(rectBeginx, 0, rectEndx, 0, COLORS, null, Shader.TileMode.MIRROR); secondRect = new RectF(rectBeginx, secondRectBeginy, rectEndx, secondRectEndy); mRectBitmap = Bitmap.createBitmap(100, 1, Bitmap.Config.RGB_565); resultColor = calculateColor(firstFingerx); Color.colorToHSV(resultColor, mHSV); }
protected void onDraw(Canvas canvas) { super.onDraw(canvas); calculateRectBitmap(); rectPaint.setShader(shader); canvas.drawRoundRect(firstRect, rectCorner, rectCorner, rectPaint); canvas.drawCircle(firstFingerx, firstRectCentre, radius, circlePaint); canvas.drawCircle(firstFingerx, firstRectCentre, radius, blankPaint); int sc = canvas.saveLayer(0, 0, windowwidth, windowheight, null, Canvas.ALL_SAVE_FLAG); mRectBitmapPaint.reset(); canvas.drawRoundRect(secondRect, rectCorner, rectCorner, mRectBitmapPaint); mRectBitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(mRectBitmap, null, secondRect, mRectBitmapPaint); canvas.restoreToCount(sc); canvas.drawCircle(secondFingerx, secondRectCentre, radius, circlePaint); canvas.drawCircle(secondFingerx, secondRectCentre, radius, blankPaint); }
两个圆角矩形是用不同的方法绘制的,
第一个是用颜色渲染做的:
在init中用
shader = new LinearGradient(rectBeginx, 0, rectEndx, 0, COLORS, null, Shader.TileMode.MIRROR);
设置矩形渲染,在ondraw中用
rectPaint.setShader(shader); canvas.drawRoundRect(firstRect, rectCorner, rectCorner, rectPaint);将这个圆角矩形画出来
第二个是用画bitmap染像素点做的:
在init中用
mRectBitmap = Bitmap.createBitmap(100, 1, Bitmap.Config.RGB_565);
创建这个bitmap块,在ondraw中首先要填充这个bitmap块的像素点,写了一个calculateRectBitmap
private void calculateRectBitmap() { for (int x = 0; x < mRectBitmap.getWidth(); x++) { for (int y = 0; y < mRectBitmap.getHeight(); y++) { float[] tmpHSV = new float[3]; tmpHSV[0] = mHSV[0]; tmpHSV[1] = 1; tmpHSV[2] = (float) x / mRectBitmap.getWidth(); mRectBitmap.setPixel(x, y, Color.HSVToColor(tmpHSV)); } } }
HSV 分别为色相,饱和度,明度,色相H就是我们第一个圆角矩形中选中的颜色,它的初始化是在init里进行的
Color.colorToHSV(resultColor, mHSV);,饱和度S即表示色彩的深浅,它的范围是0-1,0为白色,明度V表示色彩的明暗,它的范围是0-1,0为黑色。在这里我们只改变明度。根据坐标的比例来填充像素点。
接下来在ondraw里画这个圆角矩形:
int sc = canvas.saveLayer(0, 0, windowwidth, windowheight, null, Canvas.ALL_SAVE_FLAG); mRectBitmapPaint.reset(); canvas.drawRoundRect(secondRect, rectCorner, rectCorner, mRectBitmapPaint); mRectBitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(mRectBitmap, null, secondRect, mRectBitmapPaint); canvas.restoreToCount(sc);
因为是圆角,所以我们使用了图形遮盖的方法来实现圆角bitmap,其要点就是设置画笔为PorterDuff.Mode.SRC_IN,别忘了保存canvas的状态。
接下来就是滑动选颜色了,在ontouchevent里面写:
@Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (x > firstFingerx - 2 * radius && x < firstFingerx + 2 * radius && y < firstRectCentre + radius) { fingerflag = 1; canMove = true; } if (x > secondFingerx - 2 * radius && x < secondFingerx + 2 * radius && y > secondRectCentre - radius) { fingerflag = 2; canMove = true; } case MotionEvent.ACTION_MOVE: if (canMove){ if (fingerflag == 1) { if (x > rectBeginx && x < rectEndx) { firstFingerx = (int) x; } else if (x < rectBeginx) { firstFingerx = rectBeginx; } else if (x > rectEndx) { firstFingerx = rectEndx; } setBaseColor(calculateColor(firstFingerx)); if (onColorChangedListener != null) { onColorChangedListener.onColorChanged(resultColor); } invalidate(); break; } else if (fingerflag == 2) { if (x >= rectBeginx && x <= rectEndx) { secondFingerx = (int) x; } else if (x < rectBeginx) { secondFingerx = rectBeginx; } else if (x > rectEndx) { secondFingerx = rectEndx; } setColor(calculateHSVColor(secondFingerx)); if (onColorChangedListener != null) { onColorChangedListener.onColorChanged(resultColor); } invalidate(); break; } } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: canMove = false; break; } return true; }
当按下时,判断是否按在了圆形滑块处,如果是就可以拖动,如果不是就不能,滑动时,可以变颜色。这里我们设置了监听事件,可以在activity里调用,比如说我们在activity里有一个view叫colorchangeview,然后我们使用了这个colorpickerview,就可以这么写:
colorpickerView.setOnColorChangedListener(new ColorPickerView.OnColorChangedListener() { @Override public void onColorChanged(int color) { colorchangeView.setBackgroundColor(color); } });
这样我们拖动的同时,colorchangeview就可以变颜色了。
最后,说明一下变颜色的设置:
主要有这么几个方法:
private int calculateHSVColor(float x) { float[] hsv = new float[3]; hsv[0] = mHSV[0]; hsv[1] = 1; hsv[2] = (x - rectBeginx) / rectLength; return Color.HSVToColor(hsv); } private int calculateColor(float x) { int unitlength = rectLength / (COLORS.length - 1); int fingerlength = (int) (x - rectBeginx); int unit = fingerlength / unitlength; float percent = (float) fingerlength / unitlength - unit; int c0; int c1; if (unit == 6) { return COLORS[COLORS.length - 1]; } else { c0 = COLORS[unit]; c1 = COLORS[unit + 1]; } int a = ave(Color.alpha(c0), Color.alpha(c1), percent); int r = ave(Color.red(c0), Color.red(c1), percent); int g = ave(Color.green(c0), Color.green(c1), percent); int b = ave(Color.blue(c0), Color.blue(c1), percent); return Color.argb(a, r, g, b); } private int ave(int s, int d, float p) { return s + Math.round(p * (d - s)); }
滑动第一个圆角矩形时,使用calculateColor()方法由于其是线性渲染出来的,所以可以根据其坐标来计算颜色的argb值,argb即透明度,红色,绿色,蓝色,对应2位16进制数,比如说0xFFFF0000,其a为ff,r为ff,g为00,b为00,我们看到的就是红色。color和其都有对应的方法,见上面,比如Color.green()就是获取color的green值,计算各颜色的比重后再用Color.argb(a, r, g, b)把数字转为颜色。
滑动第二个圆角矩形时,使用calculateHSVColor()方法,就是直接使用hsv的转换,上文已经说过,我们只计算其v即明度的值,根据坐标计算再转换即可,使用,Color.HSVToColor(hsv)就是把一个hsv数组转化为颜色。
相关文章推荐
- android自定义手势解锁
- Android开发中Handler的经典总结
- android Gson解析
- android style
- Android 实现闪光灯开关和在之前扫描二维码包中使用方法
- 【Android】Fragment的简单笔记
- Android 消息循环源码剖析
- Android开发实现高德地图定位详解
- Android 调用Google Map api v3路由两点间距离、时间以及解析方式
- Android实现号码归属地查询
- android 股票K线图
- 王学岗自定义控件(二)
- Android开发之多线程下载文件
- Android 京东支付
- Android 开发中错误收集(一)
- SwipeBackActivity的学习遇到的问题
- Android WebView编程的那些坑(一)
- Android天气Demo
- android真机调试时,访问本地服务ip设置
- android 中SD卡访问权限问题【转自Eagle的博客】