Android 不规则按钮

各位朋友首先请移步 http://blog.csdn.net/windspeaker/article/details/8129269  看看这个效果满足你的需求否?如果是,那不用往下看了。

如果满足不了,且你有耐心,请看下面:



效果图:



这个是我公司现在的一个小项目。我也不想去担心泄露机密什么的,我觉得有必要把这些东西放到网上大家共享一下。

需求:如图,在房子(整张是一个背景图片)的瓦片上蒙一层(这一层红色的东西后期会去掉,现在只是展示一个区域而已。正因为后面会去掉红色的这一层,那么就不能用图片透明的思路来做了),然后点某一层的任何区域都会响应点击事件。当然,其他区域点击是没有效果的。

思路:

1、自定义一个View,这个View的功能就是画出这些不规则的区域;

2、把这些不规则的View加入项目 ,然后创建点击事件。

那么,问题又来了(挖掘机技术哪家强?)!如何自定义这些不规则的View?大家都知道Android的所有View都是矩形,就算你弄一个png透明的图片,那么这个图片也肯定是矩形的。你点它,不需要的区域也会响应事件(如上所说,如果不需要隐藏的话,可以移步顶部网址采用判断图片是否透明的方式来实现)。

既然这样,那就会想到这样一个解决方案了:

创建一个自定义view,然后在这个view里裁剪出一个不规则的区域,这个区域是path构建的,那这个path又是由多个坐标点连成的一个封闭空间。然后把path转换成Region,这时,Region就有一个方法region.contains(x,y)来判断传入的x,y坐标是否在这个区域内了。这个原理其实也是顶部链接里那个大哥写的思路,只是他没写得多详细而已。

OK,废话说完,看代码(只贴主要代码,其实也差不多是全部了,我不可能还把项目里的牵牵扯扯在复制出来单独搞个项目代码贴这里。。。):


步骤1(自定义不规则的那个View):

import android.app.ActionBar.LayoutParams;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.Log;
import android.view.View;

public class MyView extends View {

	Paint paint = new Paint();
	private Path path = null;

//	public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
//		super(context, attrs, defStyleAttr);
//		// TODO Auto-generated constructor stub
//	}
//
//	public MyView(Context context, AttributeSet attrs) {
//		super(context, attrs);
//		// TODO Auto-generated constructor stub
//	}

	public MyView(Context context,Path path) {
		super(context);
		this.path = path;
		// TODO Auto-generated constructor stub
	}


	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// TODO Auto-generated method stub
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		int wSize = 0;
		int hSize = 0;
		if (widthMeasureSpec == 0) {
			Log.d("tag", "widthMeasureSpec is 0,so match_parent!");
			wSize = LayoutParams.MATCH_PARENT;
		}else{
			wSize = MeasureSpec.getSize(widthMeasureSpec);
		}
		if (heightMeasureSpec == 0) {
			Log.d("tag", "heightMeasureSpec is 0,so match_parent!");
			hSize = LayoutParams.MATCH_PARENT;
		}else{
			hSize = MeasureSpec.getSize(heightMeasureSpec);
		}
		setMeasuredDimension(wSize, hSize);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		paint.setAntiAlias(true);
		paint.setColor(Color.RED);
		paint.setAlpha(100);
		canvas.clipPath(path);
		canvas.drawPath(path, paint);
	}

}

注:本人的这个View在实际中的应用大小就是 MATCH_PARENT。通过framelayout布局,你懂的。


步骤2(通过代码创建这个不规则的View):

	/***
	 * 创建不规则矩形
	 * 
	 * @param view
	 * @param listRegion
	 * @param path
	 * @param id
	 * @return
	 */
	public static MyView createAView(Context context,
			List<SparseArray<Region>> listRegion, Path path, int id) {
		Region re = new Region();
		RectF r = new RectF();
		path.computeBounds(r, true);
		// 设置区域路径和剪辑描述的区域
		re.setPath(path, new Region((int) r.left, (int) r.top, (int) r.right,
				(int) r.bottom));
		SparseArray<Region> sa = new SparseArray<Region>();
		sa.append(id, re);
		MyView myView = new MyView(context, path);
		boolean has = false;
		//当listRegion里没有这一个的时候才存进去
		for (int i = 0; i < listRegion.size(); i++) {
			if (null != listRegion.get(i).get(id)) {
				has = true;
				i = listRegion.size();
			}
		}
		if (!has) {
			listRegion.add(sa);
		}
		// MyView myView = (MyView) view.findViewById(id);
		return myView;
	}

注:这个 listRegion我是用来装所有的region,加sparseArray只是为了给每个region设置一个id。

步骤3(创建这个不规则的View):

Path path1 = SysUtil.createAPath(new int[] { 503, 847, 584, 810, 663,
				835, 699, 916, 671, 915, 716, 994, 545, 1059, 500, 909, 517,
				898 });
		MyView myView1 = SysUtil.createAView(getActivity(), AppConstant.getListSA(),
				path1, 101);

		Path path2 = SysUtil.createAPath(new int[] { 398, 766, 554, 756, 599,
				741, 666, 750, 708, 822, 665, 834, 584, 810, 506, 847, 482,
				865, 446, 789 });
		MyView myView2 = SysUtil.createAView(getActivity(), AppConstant.getListSA(),
				path2, 102);

		Path path3 = SysUtil.createAPath(new int[] { 158, 855, 280, 853, 350,
				834, 419, 844, 497, 921, 358, 958, 386, 994, 295, 1014, 227,
				882 });
		MyView myView3 = SysUtil.createAView(getActivity(), AppConstant.getListSA(),
				path3, 103);

		Path path4 = SysUtil.createAPath(new int[] { 14, 521, 251, 498, 295,
				512, 320, 573, 197, 587, 82, 622, 67, 546 });
		MyView myView4 = SysUtil.createAView(getActivity(), AppConstant.getListSA(),
				path4, 104);

		Path path5 = SysUtil.createAPath(new int[] { 236, 440, 535, 374, 623,
				384, 672, 453, 487, 507, 353, 528, 310, 462 });
		MyView myView5 = SysUtil.createAView(getActivity(), AppConstant.getListSA(),
				path5, 105);

		FrameLayout frameLayout = (FrameLayout) fragment;

		frameLayout.addView(myView1);
		frameLayout.addView(myView2);
		frameLayout.addView(myView3);
		frameLayout.addView(myView4);
		frameLayout.addView(myView5);

继续注:这些数字是怎么来的?前面说过了,每个不规则区域都是有很多个点组成的,比如:{10,20,30,40}里 ,10,20是第一个点的 x与y坐标,30,40是第二个点的。那你可能会问:这个点是怎么确定的?呵呵,我刚开始也没想到,是ios那边的“彭老师”给的一个办法,新建一工程,里面就把背景图设上去,然后加一个onTouchEvent事件,最好是用模拟器运行,然后就可以通过鼠标来在图上需要的关键位置点一下,这个点的坐标在onTouchEvent事件你自己写代码log出来就行。最后就组成了这么一个数组。 frameLayout可以理解成空的main_activity的根目录。  AppConstant.getListSA()就是前面提到用来装所以region的list而已。

步骤4(呵呵,不好意思跟步骤3反了):

	public static Path createAPath(int[] points) {
		float scaleX = 720f / 720f;
		float scaleY = 1184f / 1280f;
		Path path = new Path();
		for (int i = 0; i < points.length / 2; i++) {
			if (i == 0) {
				path.moveTo(points[i] / scaleX, points[i + 1] / scaleY);
			} else {
				path.lineTo(points[i * 2] / scaleX, points[i * 2 + 1] / scaleY);
			}
		}
		return path;
	}

这个就是上面所需的createAPath方法。但我写死了比例的。我用模拟器打点的分辨率是720*1280,但实际可用高度是1184。我手机实际可用高度是1280,后期再动态设置。


步骤5(添加点击事件):

再mainActivity里重写 onTouchEvent。你自己再判断一下当是单击的时候传一个MotionEvent到这个方法里

	/***
	 * 拿到建筑ID
	 * @param list
	 * @param motionEvent
	 * @return
	 */
	public static int getObjectId(MotionEvent motionEvent){
//		Log.d("tag", "currPage:"+AppConstant.currPage);
		int tempStart = AppConstant.currPage * 100;
		int tempEnd = (AppConstant.currPage + 1) * 100;
		int objectId = Integer.MIN_VALUE;
		for (int i = 0; i < AppConstant.getListSA().size(); i++) {
			SparseArray<Region> sa = AppConstant.getListSA().get(i);
			Region region  = sa.valueAt(0);
			if (tempStart < sa.keyAt(0) && sa.keyAt(0) <  tempEnd) {
				if (region.contains((int)motionEvent.getX(), (int)motionEvent.getY())) {
					return sa.keyAt(0);
				}
			}
		}
		return objectId;
	}
注: AppConstant.currPage你不用管,因为我这项目整个都是一个ViewPager,所以判断了一下该响应哪个页面点击事件。



OK,全文完毕。有问题请留言,不要找我要源码哈,毕竟是正式项目 ,我这代码都贴了95%了。。。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值