自定义View——水波纹倒影

时间:2023-02-09 09:15:56

一、定义属性

1.在attrXML中编写属性

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <attr name="endoffset" format="dimension" />
    <attr name="mirror_height" format="dimension" />
    <attr name="detach_WidthCount" format="integer" />
    <attr name="detach_HeightCount" format="integer" />
    <attr name="sinRange" format="float"/>
    <attr name="flagCount" format="integer"/>

    <declare-styleable name="MyMirrorView">
        <attr name="endoffset" />
        <attr name="mirror_height" />
        <attr name="detach_WidthCount" />
        <attr name="detach_HeightCount" />
        <attr name="sinRange"/>
         <attr name="flagCount"/>
    </declare-styleable>

</resources>
2.编写布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:huaihuai="http://schemas.android.com/apk/res/com.huaihuai.mydemolistapp"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.imooc.mydeclareView.MyMirrorView
        android:id="@+id/id_mirrorView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        huaihuai:detach_WidthCount="50"
        huaihuai:detach_HeightCount="50"
        huaihuai:flagCount="4"
        huaihuai:sinRange="15" 
        huaihuai:endoffset="20dp">
    </com.imooc.mydeclareView.MyMirrorView>

</LinearLayout>
3.在自定义View中引用

TypedArray array = getResources().obtainAttributes(attrs,
				R.styleable.MyMirrorView);
		endRightOffset = array.getDimension(R.styleable.MyMirrorView_endoffset,
				0);
		mirror_height = array.getDimension(
				R.styleable.MyMirrorView_mirror_height, mBitmap.getHeight());

		detach_WidthCount = array.getInt(
				R.styleable.MyMirrorView_detach_WidthCount, 200);
		detach_HeghtCount = array.getInt(
				R.styleable.MyMirrorView_detach_HeightCount, 200);
		sinRange = array.getFloat(R.styleable.MyMirrorView_sinRange, 50f);
		flagCount = array.getInt(R.styleable.MyMirrorView_flagCount, 10);
		array.recycle();
		initView();
声明View中属性
/**
	 * 最后的偏移量
	 */
	private float endRightOffset;

	/**
	 * mirror的高度
	 */
	private float mirror_height;

	/**
	 * 图片宽度分离的数目
	 */
	private int detach_WidthCount;

	/**
	 * 图片高度分离的数目
	 */
	private int detach_HeghtCount;

	/**
	 * sin的变化幅度
	 */
	private float sinRange;

	/**
	 * 驼峰数量
	 */
	private int flagCount;

	/**
	 * 图片分割后的坐标存储
	 */
	private float[] verts;
	private float[] orig;
	private float k = 1;

二、 初始化View以及数据

1。初始化View

// 默认的图片
		int mScreenWidth = context.getResources().getDisplayMetrics().widthPixels;
		int mScreenHeight = getResources().getDisplayMetrics().heightPixels;
		Bitmap mBitmap = BitmapFactory.decodeResource(getResources(),
				R.drawable.pic_5);
		float scaleY = (float) mScreenHeight / (2f * mBitmap.getHeight());
		float scaleX = (float) mScreenWidth / (mBitmap.getWidth());
		// 使图片缩放为屏幕的一半
		mBitmapOld = mBitmap = HandleImageUtils.scaleBitmap(mBitmap, scaleX,
				scaleY);
// 图片Y轴对称
		Matrix matrix = new Matrix();
		matrix.setScale(1.0f, -1.0f);
		mBitmapMirror = Bitmap.createBitmap(mBitmapOld, 0, 0,
				mBitmapOld.getWidth(), (int) mirror_height, matrix, true);
2.进行图片的分割

// 进行图片的分割
		detachImageView(mBitmapMirror);
/**
 * 进行图片的分割
*/
private void detachImageView(Bitmap bitmap) {
		// orig偶数存储fx,奇数存储fy
		verts = new float[2 * (detach_HeghtCount + 1) * (detach_WidthCount + 1)];
		orig = new float[2 * (detach_HeghtCount + 1) * (detach_WidthCount + 1)];
		float width = bitmap.getWidth();
		float heght = bitmap.getHeight();
		int index = 0;
		for (int i = 0; i < detach_HeghtCount + 1; i++) {
			float fy = heght / detach_HeghtCount * i;
			for (int j = 0; j < detach_WidthCount + 1; j++) {
				float fx = width / detach_WidthCount * j;
				orig[2 * index + 0] = verts[2 * index + 0] = fx;
				orig[2 * index + 1] = verts[2 * index + 1] = fy;
				// pos[i][j] = (float) Math.sqrt(Math.pow(fx, 2) + Math.pow(fy,
				// 2));
				index += 1;
			}
		}
	}
3.设置画笔

// 设置镜面的渲染
		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		mPaint.setShader(new LinearGradient(0, (float) (1.8 * mBitmapOld
				.getHeight()), 0, mBitmapOld.getHeight(), 0xf0000000,
				0x00000000, TileMode.CLAMP));
		mPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
三、OnDraw()进行绘制

1.绘制屏幕上半层图像

canvas.drawBitmap(mBitmapOld, 0, 0, null);

2.绘制下半层图像

canvas.drawBitmapMesh(mBitmapMirror, detach_WidthCount,
detach_HeghtCount, verts, 0, null, 0, null);

通过verts的坐标改变可以绘制任意变化的图片(关键)


3.在下半层渲染图片(线性渐变)

canvas.drawRect(0, mBitmapOld.getHeight(), mBitmapMirror.getWidth(),
mirror_height + mBitmapOld.getHeight(), mPaint);

4.添加动态效果,使镜像图片像水波波动

/**
	 * 旗帜效果
	 */
	private void sinflagView() {
		for (int i = 0; i < detach_HeghtCount + 1; i++) {
			for (int j = 0; j < detach_WidthCount + 1; j++) {
				verts[(i * (detach_HeghtCount + 1) + j) * 2 + 0] = orig[(i
						* (detach_HeghtCount + 1) + j) * 2 + 0];
						//- 0.3f * mBitmapMirror.getWidth() / 2;
				float scale = j * flagCount;
				while (scale > detach_WidthCount) {
					scale -= detach_WidthCount;
				}
				float offsetY = (float) Math.sin((float) scale
						/ detach_WidthCount * Math.PI * 2 + k * 2 * Math.PI);
				verts[(i * (detach_WidthCount + 1) + j) * 2 + 1] = orig[(i
						* (detach_WidthCount + 1) + j) * 2 + 1]
						+ sinRange * offsetY + mBitmapOld.getHeight();
				Log.i("521huaihuai", "" + (float) i / detach_HeghtCount
						* endRightOffset);
			}
		}
	}
5.不断地进行重绘

invalidate();
k += 0.1f;


demo链接http://pan.baidu.com/s/1c0PEl0W


大概这样,感觉有一些可以改变,可以新建一个类来存储verts的坐标,更容易实现坐标的改变(一维2二维)
感觉这个View改成只剩下动态倒影的bitmap,在修改一下形状,用来做头像倒是还可以,按钮的话,不确定,

今天本想通过这个,做一个,像水滴的波纹,不过,结果一塌糊涂,计算量太大,跑不起来,而且算法还有问题,还是得多多学习别人的经验。