Android开发之自定义View专题(二):自定义饼图

在图表里面,常用的图标一般为折线图、柱形图和饼图,上周,博主已经将柱形图分享。在博主的项目里面其实还用到了饼图,但没用到折线图。其实学会了其中一个,再去写其他的,应该都是知道该怎么写的,原理都是自己绘制图形,然后获取触摸位置判定点击事件。好了,废话不多说,直接上今天的饼图的效果图

这次也是博主从项目里面抽离出来的,这次的代码注释会比上次的柱形图更加的详细,更加便于有兴趣的朋友一起学习。图中的那个圆形指向箭头不属于饼图的部分,是在布局文件中为了美化另外添加进去的,有兴趣的朋友可以下载完整的项目下来研究学习。

下载地址:http://download.csdn.net/detail/victorfreedom/8322639

本来想上传到github的,但是网络不给力,过几天再上传吧。

代码部分就直接贴出自定义饼图部分,支持xml文件写入构造,也支持new方法构造。

package com.freedom.piegraph;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * @ClassName: PiegraphView
 * @author victor_freedom ([email protected])
 * @createddate 2015年1月3日 下午4:30:10
 * @Description: 自定义饼状图
 */
@SuppressLint({ "DrawAllocation" })
public class PiegraphView extends View implements Runnable {

	// 动画速度
	private float moveSpeed = 3.0F;
	// 总数值
	private double total;
	// 各饼块对应的数值
	private Double[] itemValuesTemp;
	// 各饼块对应的数值
	private Double[] itemsValues;
	// 各饼块对应的颜色
	private String[] itemColors;
	// 各饼块的角度
	private float[] itemsAngle;
	// 各饼块的起始角度
	private float[] itemsStartAngle;
	// 各饼块的占比
	private float[] itemsPercent;
	// 旋转起始角度
	private float rotateStartAng = 0.0F;
	// 旋转结束角度
	private float rotateEndAng = 0.0F;
	// 正转还是反转
	private boolean isClockWise;
	// 正在旋转
	private boolean isRotating;
	// 是否开启动画
	private boolean isAnimEnabled = true;
	// 边缘圆环的颜色
	private String loopStrokeColor;
	// 边缘圆环的宽度
	private float strokeWidth = 0.0F;
	// 饼图半径,不包括圆环
	private float radius;
	// 当前item的位置
	private int itemPostion = -1;
	// 停靠位置
	private int stopPosition = 0;
	// 停靠位置
	public static final int TO_RIGHT = 0;
	public static final int TO_BOTTOM = 1;
	public static final int TO_LEFT = 2;
	public static final int TO_TOP = 3;

	// 颜色值
	private final String[] DEFAULT_ITEMS_COLORS = { "#FF0000", "#FFFF01",
			"#FF9933", "#9967CC", "#00CCCC", "#00CC33", "#0066CC", "#FF6799",
			"#99FF01", "#FF67FF", "#4876FF", "#FF00FF", "#FF83FA", "#0000FF",
			"#363636", "#FFDAB9", "#90EE90", "#8B008B", "#00BFFF", "#FFFF00",
			"#00FF00", "#006400", "#00FFFF", "#00FFFF", "#668B8B", "#000080",
			"#008B8B" };
	// 消息接收器
	private Handler piegraphHandler = new Handler();

	// 监听器集合
	private OnPiegraphItemSelectedListener itemSelectedListener;

	public PiegraphView(Context context, String[] itemColors,
			Double[] itemSizes, float total, int radius, int strokeWidth,
			String strokeColor, int stopPosition, int separateDistence) {
		super(context);

		this.stopPosition = stopPosition;

		if ((itemSizes != null) && (itemSizes.length > 0)) {
			itemValuesTemp = itemSizes;
			this.total = total;
			// 重设总值
			reSetTotal();
			// 重设各个模块的值
			refreshItemsAngs();
		}

		if (radius < 0)
			// 默认半径设置为100
			this.radius = 100.0F;
		else {
			this.radius = radius;
		}
		// 默认圆环宽度设置为2
		if (strokeWidth < 0)
			strokeWidth = 2;
		else {
			this.strokeWidth = strokeWidth;
		}

		loopStrokeColor = strokeColor;

		if (itemColors == null) {
			// 如果没有设定颜色,则使用默认颜色值
			setDefaultColor();
		} else if (itemColors.length < itemSizes.length) {
			this.itemColors = itemColors;
			// 如果设置的颜色值和设定的集合大小不一样,那么需要充默认颜色值集合里面补充颜色,一般是不会出现这种情况。
			setDifferentColor();
		} else {
			this.itemColors = itemColors;
		}

		invalidate();
	}

	public PiegraphView(Context context, AttributeSet attrs) {
		super(context, attrs);
		loopStrokeColor = "#000000";
		// 把我们自定义的属性,放在attrs的属性集合里面
		TypedArray a = context.obtainStyledAttributes(attrs,
				R.styleable.PiegraphView);
		radius = ScreenUtil.dip2px(getContext(),
				a.getFloat(R.styleable.PiegraphView_radius, 100));
		strokeWidth = ScreenUtil.dip2px(getContext(),
				a.getFloat(R.styleable.PiegraphView_strokeWidth, 2));
		moveSpeed = a.getFloat(R.styleable.PiegraphView_moveSpeed, 5);
		if (moveSpeed < 1F) {
			moveSpeed = 1F;
		}
		if (moveSpeed > 5.0F) {
			moveSpeed = 5.0F;
		}
		invalidate();
		a.recycle();
	}

	/**
	 * @Title: setRaduis
	 * @Description: 设置半径
	 * @param radius
	 * @throws
	 */
	public void setRaduis(float radius) {
		if (radius < 0)
			this.radius = 100.0F;
		else {
			this.radius = radius;
		}
		invalidate();
	}

	public float getRaduis() {
		return radius;
	}

	/**
	 * @Title: setStrokeWidth
	 * @Description: 设置圆环宽度
	 * @param strokeWidth
	 * @throws
	 */
	public void setStrokeWidth(int strokeWidth) {
		if (strokeWidth < 0)
			strokeWidth = 2;
		else {
			this.strokeWidth = strokeWidth;
		}
		invalidate();
	}

	public float getStrokeWidth() {
		return strokeWidth;
	}

	/**
	 * @Title: setStrokeColor
	 * @Description: 设置圆环颜色
	 * @param strokeColor
	 * @throws
	 */
	public void setStrokeColor(String strokeColor) {
		loopStrokeColor = strokeColor;
		invalidate();
	}

	public String getStrokeColor() {
		return loopStrokeColor;
	}

	/**
	 * @Title: setitemColors
	 * @Description: 设置个饼块的颜色
	 * @param colors
	 * @throws
	 */
	public void setitemColors(String[] colors) {
		if ((itemsValues != null) && (itemsValues.length > 0)) {
			// 如果传入值未null,则使用默认的颜色
			if (colors == null) {
				setDefaultColor();
			} else if (colors.length < itemsValues.length) {
				// 如果传入颜色不够,则从默认颜色中填补
				itemColors = colors;
				setDifferentColor();
			} else {
				itemColors = colors;
			}
		}

		invalidate();
	}

	public String[] getitemColors() {
		return itemColors;
	}

	/**
	 * @Title: setitemsValues
	 * @Description: 设置各饼块数据
	 * @param items
	 * @throws
	 */
	public void setitemsValues(Double[] items) {
		if ((items != null) && (items.length > 0)) {
			itemValuesTemp = items;
			// 重设总值,默认为所有值的和
			reSetTotal();
			refreshItemsAngs();
			setitemColors(itemColors);
		}
		invalidate();
	}

	public Double[] getitemsValues() {
		return itemValuesTemp;
	}

	public void setTotal(int total) {
		this.total = total;
		reSetTotal();

		invalidate();
	}

	public double getTotal() {
		return total;
	}

	/**
	 * @Title: setAnimEnabled
	 * @Description: 设置是否开启旋转动画
	 * @param isAnimEnabled
	 * @throws
	 */
	public void setAnimEnabled(boolean isAnimEnabled) {
		this.isAnimEnabled = isAnimEnabled;
		invalidate();
	}

	public boolean isAnimEnabled() {
		return isAnimEnabled;
	}

	public void setmoveSpeed(float moveSpeed) {
		if (moveSpeed < 1F) {
			moveSpeed = 1F;
		}
		if (moveSpeed > 5.0F) {
			moveSpeed = 5.0F;
		}
		this.moveSpeed = moveSpeed;
	}

	public float getmoveSpeed() {
		if (isAnimEnabled()) {
			return moveSpeed;
		}
		return 0.0F;
	}

	/**
	 * @Title: setShowItem
	 * @Description: 旋转到指定位置的item
	 * @param position
	 *            位置
	 * @param anim
	 *            是否动画
	 * @param listen
	 *            是否设置监听器
	 * @throws
	 */
	public void setShowItem(int position, boolean anim) {
		if ((itemsValues != null) && (position < itemsValues.length)
				&& (position >= 0)) {
			// 拿到需要旋转的角度
			rotateEndAng = getLastrotateStartAngle(position);
			itemPostion = position;

			if (anim) {
				rotateStartAng = 0.0F;
				if (rotateEndAng > 0.0F) {
					// 如果旋转角度大于零,则顺时针旋转
					isClockWise = true;
				} else {
					// 如果小于零则逆时针旋转
					isClockWise = false;
				}
				// 开始旋转
				isRotating = true;
			} else {
				rotateStartAng = rotateEndAng;
			}

			// 如果有监听器
			if (null != itemSelectedListener) {
				itemSelectedListener.onPieChartItemSelected(position,
						itemColors[position], itemsValues[position],
						itemsPercent[position],
						getAnimTime(Math.abs(rotateEndAng - rotateStartAng)));
			}
			// 开始旋转
			piegraphHandler.postDelayed(this, 1L);
		}
	}

	private float getLastrotateStartAngle(int position) {
		float result = 0.0F;
		// 拿到旋转角度,根据停靠位置进行修正
		result = itemsStartAngle[position] + itemsAngle[position] / 2.0F
				+ getstopPositionAngle();
		if (result >= 360.0F) {
			result -= 360.0F;
		}

		if (result <= 180.0F)
			result = -result;
		else {
			result = 360.0F - result;
		}

		return result;
	}

	/**
	 * @Title: getstopPositionAngle
	 * @Description: 根据停靠位置修正旋转角度
	 * @return
	 * @throws
	 */
	private float getstopPositionAngle() {
		float resultAngle = 0.0F;
		switch (stopPosition) {
		case TO_RIGHT:
			resultAngle = 0.0F;
			break;
		case TO_LEFT:
			resultAngle = 180.0F;
			break;
		case TO_TOP:
			resultAngle = 90.0F;
			break;
		case TO_BOTTOM:
			resultAngle = 270.0F;
			break;
		}

		return resultAngle;
	}

	public int getShowItem() {
		return itemPostion;
	}

	public void setstopPosition(int stopPosition) {
		this.stopPosition = stopPosition;
	}

	public int getstopPosition() {
		return stopPosition;
	}

	/**
	 * @Title: refreshItemsAngs
	 * @Description: 初始化各个角度
	 * @throws
	 */
	private void refreshItemsAngs() {
		if ((itemValuesTemp != null) && (itemValuesTemp.length > 0)) {
			// 如果出现总值比设定的集合的总值还大,那么我们自动的增加一个模块出来(几乎不会出现这种情况)
			if (getTotal() > getAllSizes()) {
				itemsValues = new Double[itemValuesTemp.length + 1];
				for (int i = 0; i < itemValuesTemp.length; i++) {
					itemsValues[i] = itemValuesTemp[i];
				}
				itemsValues[(itemsValues.length - 1)] = (getTotal() - getAllSizes());
			} else {
				itemsValues = new Double[itemValuesTemp.length];
				itemsValues = itemValuesTemp;
			}

			// 开始给各模块赋值
			itemsPercent = new float[itemsValues.length];
			itemsStartAngle = new float[itemsValues.length];
			itemsAngle = new float[itemsValues.length];
			float startAngle = 0.0F;

			for (int i = 0; i < itemsValues.length; i++) {
				itemsPercent[i] = ((float) (itemsValues[i] * 1.0D / getTotal() * 1.0D));
			}

			for (int i = 0; i < itemsPercent.length; i++) {
				itemsAngle[i] = (360.0F * itemsPercent[i]);
				if (i != 0) {
					itemsStartAngle[i] = startAngle + itemsAngle[i - 1];
					startAngle = 360.0F * itemsPercent[(i - 1)] + startAngle;
				} else {
					// Android默认起始位置设定是右侧水平,初始化默认停靠位置也在右边。有兴趣的同学可以根据自己的喜好修改
					itemsStartAngle[i] = -itemsAngle[i] / 2;
					startAngle = itemsStartAngle[i];
				}
			}
		}
	}

	/**
	 * 绘图
	 */
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		// 饼图半径加圆环半径
		float realRadius = radius + strokeWidth;
		Paint paint = new Paint();
		paint.setAntiAlias(true);
		float lineLength = 2.0F * radius + strokeWidth;
		if (strokeWidth != 0.0F) {
			// 空心的画笔,先画外层圆环
			paint.setStyle(Paint.Style.STROKE);
			paint.setColor(Color.parseColor(loopStrokeColor));
			paint.setStrokeWidth(strokeWidth);
			canvas.drawCircle(realRadius, realRadius, realRadius - 5, paint);
		}

		if ((itemsAngle != null) && (itemsStartAngle != null)) {
			// 旋转角度
			canvas.rotate(rotateStartAng, realRadius, realRadius);
			// 设定饼图矩形
			RectF oval = new RectF(strokeWidth, strokeWidth, lineLength,
					lineLength);
			// 开始画各个扇形
			for (int i = 0; i < itemsAngle.length; i++) {
				oval = new RectF(strokeWidth, strokeWidth, lineLength,
						lineLength);
				// 先画实体
				paint.setStyle(Paint.Style.FILL);
				paint.setColor(Color.parseColor(itemColors[i]));
				canvas.drawArc(oval, itemsStartAngle[i], itemsAngle[i], true,
						paint);
				// 再画空心体描边
				paint.setStyle(Paint.Style.STROKE);
				paint.setStrokeWidth(strokeWidth / 2);
				paint.setColor(Color.WHITE);
				canvas.drawArc(oval, itemsStartAngle[i], itemsAngle[i], true,
						paint);

			}
		}
		// 画中心的小圆
		paint.setStyle(Paint.Style.FILL);
		paint.setColor(Color.LTGRAY);
		canvas.drawCircle(realRadius, realRadius,
				ScreenUtil.dip2px(getContext(), 40), paint);
		// 描边
		paint.setStyle(Paint.Style.STROKE);
		paint.setColor(Color.WHITE);
		paint.setStrokeWidth(strokeWidth);
		canvas.drawCircle(realRadius, realRadius,
				ScreenUtil.dip2px(getContext(), 40), paint);

	}

	/**
	 * 触摸事件
	 */
	public boolean onTouchEvent(MotionEvent event) {
		if ((!isRotating) && (itemsValues != null) && (itemsValues.length > 0)) {
			float x1 = 0.0F;
			float y1 = 0.0F;
			switch (event.getAction()) {
			// 按下
			case MotionEvent.ACTION_DOWN:
				x1 = event.getX();
				y1 = event.getY();
				float r = radius + strokeWidth;
				if ((x1 - r) * (x1 - r) + (y1 - r) * (y1 - r) - r * r <= 0.0F) {
					// 拿到位置
					int position = getShowItem(getTouchedPointAngle(r, r, x1,
							y1));
					// 旋转到指定位置
					setShowItem(position, isAnimEnabled());
				}
				break;
			}

		}

		return super.onTouchEvent(event);
	}

	/**
	 * @Title: getTouchedPointAngle
	 * @Description: 计算触摸角度
	 * @param radiusX
	 *            圆心
	 * @param radiusY
	 *            圆心
	 * @param x1
	 *            触摸点
	 * @param y1
	 *            触摸点
	 * @return
	 * @throws
	 */
	private float getTouchedPointAngle(float radiusX, float radiusY, float x1,
			float y1) {
		float differentX = x1 - radiusX;
		float differentY = y1 - radiusY;
		double a = 0.0D;
		double t = differentY
				/ Math.sqrt(differentX * differentX + differentY * differentY);

		if (differentX > 0.0F) {
			// 0~90
			if (differentY > 0.0F)
				a = 6.283185307179586D - Math.asin(t);
			else
				// 270~360
				a = -Math.asin(t);
		} else if (differentY > 0.0F)
			// 90~180
			a = 3.141592653589793D + Math.asin(t);
		else {
			// 180~270
			a = 3.141592653589793D + Math.asin(t);
		}
		return (float) (360.0D - a * 180.0D / 3.141592653589793D % 360.0D);
	}

	/**
	 * @Title: getShowItem
	 * @Description: 拿到触摸位置
	 * @param touchAngle
	 *            触摸位置角度
	 * @return
	 * @throws
	 */
	private int getShowItem(float touchAngle) {
		int position = 0;
		for (int i = 0; i < itemsStartAngle.length; i++) {
			if (i != itemsStartAngle.length - 1) {
				if ((touchAngle >= itemsStartAngle[i])
						&& (touchAngle < itemsStartAngle[(i + 1)])) {
					position = i;
					break;
				}

			} else if ((touchAngle > itemsStartAngle[(itemsStartAngle.length - 1)])
					&& (touchAngle < itemsStartAngle[0])) {
				position = itemsValues.length - 1;
			} else {
				// 如果触摸位置不对,则旋转到最大值得位置
				position = getPointItem(itemsStartAngle);
			}

		}

		return position;
	}

	private int getPointItem(float[] startAngle) {
		int item = 0;

		float temp = startAngle[0];
		for (int i = 0; i < startAngle.length - 1; i++) {
			if (startAngle[(i + 1)] - temp > 0.0F)
				temp = startAngle[i];
			else {
				return i;
			}
		}

		return item;
	}

	protected void onDetachedFromWindow() {
		super.onDetachedFromWindow();
		piegraphHandler.removeCallbacks(this);
	}

	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		float widthHeight = 2.0F * (radius + strokeWidth + 1.0F);
		// 重设view的宽高
		setMeasuredDimension((int) widthHeight, (int) widthHeight);
	}

	/**
	 * 旋转动作
	 */
	public void run() {
		if (isClockWise) {
			// 顺时针旋转
			rotateStartAng += moveSpeed;
			invalidate();
			piegraphHandler.postDelayed(this, 10L);
			if (rotateStartAng - rotateEndAng >= 0.0F) {
				rotateStartAng = 0.0F;
				// 如果已经转到指定位置,则停止动画
				piegraphHandler.removeCallbacks(this);
				// 重设各模块起始角度值
				resetStartAngle(rotateEndAng);
				isRotating = false;
			}
		} else {
			// 逆时针旋转
			rotateStartAng -= moveSpeed;
			invalidate();
			piegraphHandler.postDelayed(this, 10L);
			if (rotateStartAng - rotateEndAng <= 0.0F) {
				rotateStartAng = 0.0F;
				piegraphHandler.removeCallbacks(this);
				resetStartAngle(rotateEndAng);

				isRotating = false;
			}
		}
	}

	private float getAnimTime(float ang) {
		return (int) Math.floor(ang / getmoveSpeed() * 10.0F);
	}

	/**
	 * @Title: resetStartAngle
	 * @Description: 重设个模块角度
	 * @param angle
	 * @throws
	 */
	private void resetStartAngle(float angle) {
		for (int i = 0; i < itemsStartAngle.length; i++) {
			float newStartAngle = itemsStartAngle[i] + angle;

			if (newStartAngle < 0.0F)
				itemsStartAngle[i] = (newStartAngle + 360.0F);
			else if (newStartAngle > 360.0F)
				itemsStartAngle[i] = (newStartAngle - 360.0F);
			else
				itemsStartAngle[i] = newStartAngle;
		}
	}

	/**
	 * @Title: setDefaultColor
	 * @Description: 设置默认颜色
	 * @throws
	 */
	private void setDefaultColor() {
		if ((itemsValues != null) && (itemsValues.length > 0)
				&& (itemColors == null)) {
			itemColors = new String[itemsValues.length];
			if (itemColors.length <= DEFAULT_ITEMS_COLORS.length) {
				System.arraycopy(DEFAULT_ITEMS_COLORS, 0, itemColors, 0,
						itemColors.length);
			} else {
				int multiple = itemColors.length / DEFAULT_ITEMS_COLORS.length;
				int difference = itemColors.length
						% DEFAULT_ITEMS_COLORS.length;

				for (int a = 0; a < multiple; a++) {
					System.arraycopy(DEFAULT_ITEMS_COLORS, 0, itemColors, a
							* DEFAULT_ITEMS_COLORS.length,
							DEFAULT_ITEMS_COLORS.length);
				}
				if (difference > 0)
					System.arraycopy(DEFAULT_ITEMS_COLORS, 0, itemColors,
							multiple * DEFAULT_ITEMS_COLORS.length, difference);
			}
		}
	}

	/**
	 * @Title: setDifferentColor
	 * @Description: 补差颜色
	 * @throws
	 */
	private void setDifferentColor() {
		if ((itemsValues != null) && (itemsValues.length > itemColors.length)) {
			String[] preitemColors = new String[itemColors.length];
			preitemColors = itemColors;
			int leftall = itemsValues.length - itemColors.length;
			itemColors = new String[itemsValues.length];
			System.arraycopy(preitemColors, 0, itemColors, 0,
					preitemColors.length);

			if (leftall <= DEFAULT_ITEMS_COLORS.length) {
				System.arraycopy(DEFAULT_ITEMS_COLORS, 0, itemColors,
						preitemColors.length, leftall);
			} else {
				int multiple = leftall / DEFAULT_ITEMS_COLORS.length;
				int left = leftall % DEFAULT_ITEMS_COLORS.length;
				for (int a = 0; a < multiple; a++) {
					System.arraycopy(DEFAULT_ITEMS_COLORS, 0, itemColors, a
							* DEFAULT_ITEMS_COLORS.length,
							DEFAULT_ITEMS_COLORS.length);
				}
				if (left > 0) {
					System.arraycopy(DEFAULT_ITEMS_COLORS, 0, itemColors,
							multiple * DEFAULT_ITEMS_COLORS.length, left);
				}
			}
			preitemColors = null;
		}
	}

	/**
	 * @Title: reSetTotal
	 * @Description: 重设总值
	 * @throws
	 */
	private void reSetTotal() {
		double totalSizes = getAllSizes();
		if (getTotal() < totalSizes)
			total = totalSizes;
	}

	private double getAllSizes() {
		float tempAll = 0.0F;
		if ((itemValuesTemp != null) && (itemValuesTemp.length > 0)) {
			for (double itemsize : itemValuesTemp) {
				tempAll += itemsize;
			}
		}

		return tempAll;
	}

	public void setItemSelectedListener(
			OnPiegraphItemSelectedListener itemSelectedListener) {
		this.itemSelectedListener = itemSelectedListener;
	}

}

自定义View专题报表类的view到此就讲完了。博主没有写过自定义的折线图。但是学会了这两个图形的话再去自己写折线图我想也是不难的。

后续还有2期的自定义view的专题。一期是关于自定义gridView的(可以拖动gridView,但是不是和网上其他的那种拖动item,而是将item里面的内容拖动切换位置),一期是关于自定义viewGroup(类似线性布局,相对布局那种,可以往里面添加控件的)。希望能够帮助到看到此篇文章的人。

时间: 01-02

Android开发之自定义View专题(二):自定义饼图的相关文章

Android开发之自定义View专题(三):自定义GridView

gridview作为android开发中常用的组件,其功能十分强大.但是,我们有时候有很多特殊的需求,需要在其基础上进行改造.有时候会有移动gridView中item位置的需求,这个网上已经有很多例子,博主就不在描述.今天博主讲的是移动gridView中item中的内容.博主没看过网上那些移动item位置的demo,不知道其原理是不是和博主想的一样.博主思考过,似乎博主的这种实现原理似乎也可以用作实现移动item位置.而之前博主百思不得其解的小米手机的桌面的自定义乱序排放,似乎也可以用这个原理去

Android开发之自定义View专题(四):自定义ViewGroup

有时候,我们会有这样的需求,一个activity里面需要有两个或者多个界面切换,就像Viewpager那样.但是在这些界面里面又需要能够有listView,gridview等组件.如果是纵向的,似乎还好,没什么影响,那么如果是横向的,那么就会出事情.因为Viewpager会拦截触摸事件.而如果将Viewpager的触摸事件拦截掉给里面的子控件,那么Viewpager又不能响应滑动事件了.那么如何又能让界面之间能够来回切换,又能让里面的子控件的触摸事件也能毫无影响的响应呢,这个时候,我们需要自定义

android开发最常用例子整理----(1)自定义按钮实现

android开发最常用例子整理----(1)自定义按钮实现 一.Activity MainActivity.java源码: public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }

android开发最常用例子整理----(2)自定义ListView(SimpleAdapter实现)

android开发最常用例子整理----(2)自定义ListView(SimpleAdapter实现) 一.Activity MainActivity.java源码: public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layou

自定义view(二)

这里是自定义view(二),上一篇关于自定义view的一些基本知识,比如说自定义view的步骤.会涉及到哪些函数以及如何实现自定义属性,同时实现了一个很基础的自定义控件,一个自定义的计时器,需要看的人可以点击这个链接:http://www.cnblogs.com/YaoJianXun/p/5806926.html. 这次讲的是如何通过坐标系的变化实现一些更复杂的自定义view绘制,上一次博客我们实现了一个类似于计时器的环形控件,这次我们在那个基础上再做一次改动,通过坐标系的变动实现下面的效果:

Android开发之图片处理专题(二):利用AsyncTask和回调接口实现图片的异步加载和压缩

在上一篇专题Android开发之图片处理专题(一):利用软引用构建图片高速缓存中我们讲述了如何利用软引用技术构建高速缓存.那么想要用到图片,首先得有图片的来源.一般而言,一个应用的图片资源都是从服务器处获得的.今天,我们利用Android开发之网络请求通信专题(二):基于HttpClient的文件上传下载里面封装好的httpUtils来实现图片的下载,然后加载到本地配合软引用缓存使用,以一个listView为例子来说明. 一.准备工作 我们需要准备以下几个类(图片对象和软引用缓存类请参考上一篇专

Android开发之图片处理专题(三):利用ThreadPoolExcutor线程池实现多图片的异步加载

在上一篇专题Android开发之图片处理专题(二):利用AsyncTask和回调接口实现图片的异步加载和压缩中我们实现了listView的图片的大量加载.今天,我们换一种方式,采用线程池的方式来实现. 我们需要准备两个东西: 1.图片下载任务类 2.线程池. 1.图片下载任务类. 图片下载任务类,将需要显示的iamgeView,线程通讯消息管理者handler进行了封装.当图片下载无论成功还是失败,handler发送对应的消息,传入的iamgeView显示对应的图片.这里就不在应用软引用技术,采

Android自定义View(二)

前言 魅族手机的闹钟应用中有个倒计时,这个控件还是蛮有趣的.左边是魅族闹钟,右边是我们最终实现的效果,虽然有些细节还需优化,不过基本上已经达到了想要的效果,我们先来就来看看如何实现吧. 分析 确定宽高 对一个Android自定义控件来说,一般都经过三个步骤 onLayout() onMeasure() onDraw() onLayout明确子控件在父控件中的位置(本控件不需要重写),onMeasure是确定控件的大小(宽.高),而onDraw是我们重点关注的方法,我们需要在这个方法中写入显示Vi

Android进阶之自定义View实战(二)九宫格手势解锁实现

一.引言 在上篇博客Android进阶之自定义View实战(一)仿iOS UISwitch控件实现中我们主要介绍了自定义View的最基本的实现方法.作为自定义View的入门篇,仅仅介绍了Canvas的基本使用方法,而对用户交互层面仅仅处理了单击事件接口,在实际的业务中,常常涉及到手势操作,本篇博客以九宫格手势解锁View为例,来说明自定义View如何根据需求处理用户的手势操作.虽然九宫格手势解锁自定义View网上资料有很多,实现原理大同小异,但这里我只是根据自己觉得最优的思路来实现它,目的是让更