工具类
import android.content.Context; import android.util.DisplayMetrics; import android.view.WindowManager; public class UiUtils { static public int getScreenWidthPixels(Context context) { DisplayMetrics dm = new DisplayMetrics(); ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay() .getMetrics(dm); return dm.widthPixels; } static public int dipToPx(Context context, int dip) { return (int) (dip * getScreenDensity(context) + 0.5f); } static public float getScreenDensity(Context context) { try { DisplayMetrics dm = new DisplayMetrics(); ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay() .getMetrics(dm); return dm.density; } catch (Exception e) { return DisplayMetrics.DENSITY_DEFAULT; } } } 自定义Viewimport android.content.Context; import android.graphics.Canvas; import android.graphics.DrawFilter; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.PaintFlagsDrawFilter; import android.util.AttributeSet; import android.view.View; public class DynamicWave extends View { // ������ɫ private static final int WAVE_PAINT_COLOR = 0x880000aa; // y = Asin(wx+b)+h private static final float STRETCH_FACTOR_A = 20; private static final int OFFSET_Y = 0; // ��һ��ˮ���ƶ��ٶ� private static final int TRANSLATE_X_SPEED_ONE = 7; // �ڶ���ˮ���ƶ��ٶ� private static final int TRANSLATE_X_SPEED_TWO = 5; private float mCycleFactorW; private int mTotalWidth, mTotalHeight; private float[] mYPositions; private float[] mResetOneYPositions; private float[] mResetTwoYPositions; private int mXOffsetSpeedOne; private int mXOffsetSpeedTwo; private int mXOneOffset; private int mXTwoOffset; private Paint mWavePaint; private DrawFilter mDrawFilter; public DynamicWave(Context context, AttributeSet attrs) { super(context, attrs); // ��dpת��Ϊpx�����ڿ��Ʋ�ͬ�ֱ������ƶ��ٶȻ���һ�� mXOffsetSpeedOne = UiUtils.dipToPx(context, TRANSLATE_X_SPEED_ONE); mXOffsetSpeedTwo = UiUtils.dipToPx(context, TRANSLATE_X_SPEED_TWO); // ��ʼ���Ʋ��ƵĻ��� mWavePaint = new Paint(); // ȥ�����ʾ�� mWavePaint.setAntiAlias(true); // ���÷��Ϊʵ�� mWavePaint.setStyle(Style.FILL); // ���û�����ɫ mWavePaint.setColor(WAVE_PAINT_COLOR); mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // ��canvas����ȥ������ʱ��� canvas.setDrawFilter(mDrawFilter); resetPositonY(); for (int i = 0; i < mTotalWidth; i++) { // ��400ֻ��Ϊ�˿��Ʋ��ƻ��Ƶ�y������Ļ��λ�ã���ҿ��Ըij�һ��������Ȼ��̬�ı�����������Ӷ��γɲ��������½�Ч�� // ���Ƶ�һ��ˮ���� canvas.drawLine(i, mTotalHeight - mResetOneYPositions[i] - 400, i, mTotalHeight, mWavePaint); // ���Ƶڶ���ˮ���� canvas.drawLine(i, mTotalHeight - mResetTwoYPositions[i] - 400, i, mTotalHeight, mWavePaint); } // �ı��������Ƶ��ƶ��� mXOneOffset += mXOffsetSpeedOne; mXTwoOffset += mXOffsetSpeedTwo; // ����Ѿ��ƶ�����β��������ͷ��¼ if (mXOneOffset >= mTotalWidth) { mXOneOffset = 0; } if (mXTwoOffset > mTotalWidth) { mXTwoOffset = 0; } // ����view�ػ棬һ����Կ����ӳ�20-30ms�ػ棬�ճ�ʱ��Ƭ postInvalidate(); } private void resetPositonY() { // mXOneOffset����ǰ��һ��ˮ����Ҫ�ƶ��ľ��� int yOneInterval = mYPositions.length - mXOneOffset; // ʹ��System.arraycopy��ʽ��������һ�����Ƶ����� System.arraycopy(mYPositions, mXOneOffset, mResetOneYPositions, 0, yOneInterval); System.arraycopy(mYPositions, 0, mResetOneYPositions, yOneInterval, mXOneOffset); int yTwoInterval = mYPositions.length - mXTwoOffset; System.arraycopy(mYPositions, mXTwoOffset, mResetTwoYPositions, 0, yTwoInterval); System.arraycopy(mYPositions, 0, mResetTwoYPositions, yTwoInterval, mXTwoOffset); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // ��¼��view�Ŀ�� mTotalWidth = w; mTotalHeight = h; // ���ڱ���ԭʼ���Ƶ�yֵ mYPositions = new float[mTotalWidth]; // ���ڱ��沨��һ��yֵ mResetOneYPositions = new float[mTotalWidth]; // ���ڱ��沨�ƶ���yֵ mResetTwoYPositions = new float[mTotalWidth]; // �����ڶ�Ϊview�ܿ�� mCycleFactorW = (float) (2 * Math.PI / mTotalWidth); for (int i = 0; i < mTotalWidth; i++) { mYPositions[i] = (float) (STRETCH_FACTOR_A * Math.sin(mCycleFactorW * i) + OFFSET_Y); } } }import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.util.AttributeSet; import android.view.View; import com.example.myapplication.R; public class PorterDuffXfermodeView extends View { private static final int WAVE_TRANS_SPEED = 4; private Paint mBitmapPaint, mPicPaint; private int mTotalWidth, mTotalHeight; private int mCenterX, mCenterY; private int mSpeed; private Bitmap mSrcBitmap; private Rect mSrcRect, mDestRect; private PorterDuffXfermode mPorterDuffXfermode; private Bitmap mMaskBitmap; private Rect mMaskSrcRect, mMaskDestRect; private PaintFlagsDrawFilter mDrawFilter; private int mCurrentPosition; public PorterDuffXfermodeView(Context context, AttributeSet attrs) { super(context, attrs); initPaint(); initBitmap(); mPorterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN); mSpeed = UiUtils.dipToPx(context , WAVE_TRANS_SPEED); mDrawFilter = new PaintFlagsDrawFilter(Paint.ANTI_ALIAS_FLAG, Paint.DITHER_FLAG); new Thread() { public void run() { while (true) { // ���ϸı���ƵIJ��˵�λ�� mCurrentPosition += mSpeed; if (mCurrentPosition >= mSrcBitmap.getWidth()) { mCurrentPosition = 0; } try { // Ϊ�˱�֤Ч����ͬʱ�������ܽ�cpu�ճ���������������ʹ�� Thread.sleep(30); } catch (InterruptedException e) { } postInvalidate(); } }; }.start(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // ��canvas����ȥ����� canvas.setDrawFilter(mDrawFilter); canvas.drawColor(Color.TRANSPARENT); /* * �����Ʋ������浽�µ�ͼ�� */ int sc = canvas.saveLayer(0, 0, mTotalWidth, mTotalHeight, null, Canvas.ALL_SAVE_FLAG); // �趨Ҫ���ƵIJ��Ʋ��� mSrcRect.set(mCurrentPosition, 0, mCurrentPosition + mCenterX, mTotalHeight); // ���Ʋ��Ʋ��� canvas.drawBitmap(mSrcBitmap, mSrcRect, mDestRect, mBitmapPaint); // ����ͼ��Ļ��ģʽ mBitmapPaint.setXfermode(mPorterDuffXfermode); // ��������Բ canvas.drawBitmap(mMaskBitmap, mMaskSrcRect, mMaskDestRect, mBitmapPaint); mBitmapPaint.setXfermode(null); canvas.restoreToCount(sc); } // ��ʼ��bitmap private void initBitmap() { mSrcBitmap = ((BitmapDrawable) getResources().getDrawable(R.drawable.wave_2000)) .getBitmap(); mMaskBitmap = ((BitmapDrawable) getResources().getDrawable( R.drawable.circle_500)) .getBitmap(); } // ��ʼ������paint private void initPaint() { mBitmapPaint = new Paint(); // ������ mBitmapPaint.setDither(true); // ����ͼ����� mBitmapPaint.setFilterBitmap(true); mPicPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPicPaint.setDither(true); mPicPaint.setColor(Color.RED); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mTotalWidth = w; mTotalHeight = h; mCenterX = mTotalWidth / 2; mCenterY = mTotalHeight / 2; mSrcRect = new Rect(); mDestRect = new Rect(0, 0, mTotalWidth, mTotalHeight); int maskWidth = mMaskBitmap.getWidth(); int maskHeight = mMaskBitmap.getHeight(); mMaskSrcRect = new Rect(0, 0, maskWidth, maskHeight); mMaskDestRect = new Rect(0, 0, mTotalWidth, mTotalHeight); } }import android.app.Activity; import android.os.Bundle; import com.example.myapplication.R; public class PorterDuffXfermodeActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.porter_duff_xfermode_layout); } } MaiActivity代码import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import com.example.myapplication.R; public class MainActivity1 extends Activity implements OnClickListener { private View mWaveDemo; private View mXfermode; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main1); initViews(); } private void initViews() { mWaveDemo = findViewById(R.id.wave_demo); mWaveDemo.setOnClickListener(this); mXfermode = findViewById(R.id.xfermode_demo); mXfermode.setOnClickListener(this); } @Override public void onClick(View v) { if (v == mWaveDemo) { Intent intent = new Intent(this, WaveDemoActivity.class); startActivity(intent); } else if (v == mXfermode) { Intent intent = new Intent(this, PorterDuffXfermodeActivity.class); startActivity(intent); } } }import android.app.Activity; import android.os.Bundle; import com.example.myapplication.R; public class WaveDemoActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.wave_demo_layout); } }
XML布局文件
activity_mai<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:id="@+id/wave_demo" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="标准正余弦波纹" android:textSize="20dp" /> <Button android:id="@+id/xfermode_demo" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/wave_demo" android:gravity="center" android:text="图像混合模式之遮罩实现圆形波纹" android:textSize="20dp" /> </RelativeLayout> 自定义acitivty布局<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <com.example.myapplication.beisaier.PorterDuffXfermodeView android:id="@+id/porter_duff_xfermode_view" android:layout_width="173dp" android:layout_height="173dp" android:layout_gravity="center_horizontal"/> <ScrollView android:layout_width="match_parent" android:layout_height="wrap_content" > <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="20dp" android:gravity="center" android:padding="10dp" android:text="通过类PS遮罩实现优化大师水波纹效果" android:textColor="@android:color/black" android:textSize="15dp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="10dp" android:gravity="center" android:padding="10dp" android:text="android:图像混合模式" android:textColor="#ee00ff00" android:textSize="15dp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="10dp" android:gravity="center" android:padding="10dp" android:text="该方式实现有如下好处:" android:textColor="@android:color/black" android:textSize="15dp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="8dp" android:gravity="center" android:padding="8dp" android:text="1.不受硬件加速影响" android:textColor="@android:color/black" android:textSize="15dp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="8dp" android:gravity="center" android:padding="8dp" android:text="2.不必考虑绘制超出圆的问题" android:textColor="@android:color/black" android:textSize="15dp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="8dp" android:gravity="center" android:padding="8dp" android:text="3.不需要图像和ARC拼接而产生大量的计算" android:textColor="@android:color/black" android:textSize="15dp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="8dp" android:gravity="center" android:padding="8dp" android:text="4.不会出现部分机型上绘制不出的问题" android:textColor="@android:color/black" android:textSize="15dp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="8dp" android:gravity="center" android:padding="8dp" android:text="注:改变下图片绘制的位置则形成水波上涨下降动效" android:textColor="@android:color/black" android:textSize="15dp" /> </LinearLayout> </ScrollView> </LinearLayout><?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <com.example.myapplication.beisaier.DynamicWave android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>