先想大家推荐一个app叫做Morning routine,是最美应用推荐的,非常好看的一款闹钟工具(可能根据个人喜好而不相同)。这一篇是实现的一个小功能,窗帘样式的View
按照国际惯例,代码如下
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.os.Build;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
public class BitmapMeshView extends View {
private Bitmap bitmap;
private Bitmap shadowMask;
private Paint paint;
private Shader maskShader;
private int maxAlpha = 0xFF;
private int width, height;
private int bitmapWidth = 30;
private int bitmapHeight = 10;
private int touchX;
private int touchY;
private boolean newApiFlag;
private int delayOffsetX;
private AccelerateInterpolator interpolator;
private Handler handler = new Handler();
private Runnable delayRunnable = new Runnable() {
@Override
public void run() {
delayOffsetX += (touchX - delayOffsetX) * 0.5F;
handler.postDelayed(this, 20);
invalidate();
}
};
public BitmapMeshView(Context context) {
this(context, null);
}
public BitmapMeshView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@SuppressLint("NewApi")
public BitmapMeshView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.snipper);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
handler.post(delayRunnable);
newApiFlag = Build.VERSION.SDK_INT >= 18;
interpolator = new AccelerateInterpolator();
if (!newApiFlag) {
// 硬件加速不支持drawBitmapMesh的colors绘制的情况下,在原bitmap的上层覆盖一个半透明带阴影的bitmap以实现阴影功能
shadowMask =
Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas maskCanvas = new Canvas(shadowMask);
float singleWave = bitmap.getWidth() / bitmapWidth * 6.28F;
int blockPerWave = (int) (singleWave / (bitmap.getWidth() / bitmapWidth));
if (blockPerWave % 2 == 0)
blockPerWave++;
float offset =
(float) ((bitmap.getWidth() / singleWave - Math.floor(bitmap.getWidth()
/ singleWave)) * singleWave);
int[] colors = new int[blockPerWave];
float[] offsets = new float[blockPerWave];
Log.d("singleWave:" + singleWave, "blockPerWave:" + blockPerWave);
float perOffset = 1.0F / blockPerWave;
int halfWave = (int) Math.floor((float) blockPerWave / 2.0F);
int perAlpha = maxAlpha / (halfWave - 1);
for (int i = -halfWave; i < halfWave + 1; i++) {
int ii = halfWave - Math.abs(i);
int iii = i + halfWave;
colors[iii] =
(int) (perAlpha * Math.sin((float) ii / (float) blockPerWave * 3.14F)) << 24;
offsets[iii] = perOffset * iii;
Log.d("index:" + i, "colors:0x" + Integer.toHexString(colors[iii]) + ", offset:"
+ offsets[iii]);
}
maskShader =
new LinearGradient(offset, 0, singleWave + offset, 0, colors, offsets,
Shader.TileMode.REPEAT);
paint.setShader(maskShader);
maskCanvas.drawRect(0, 0, bitmap.getWidth(), bitmap.getHeight(), paint);
paint.setShader(null);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = MeasureSpec.getSize(widthMeasureSpec);
height = MeasureSpec.getSize(heightMeasureSpec);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
touchX = (int) event.getX();
touchY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
touchX = (int) event.getX();
touchY = (int) event.getY();
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
break;
}
invalidate();
return true;
}
@Override
public void onDraw(Canvas canvas) {
float[] verts = new float[(bitmapWidth + 1) * (bitmapHeight + 1) * 2];
int[] colors = new int[(bitmapWidth + 1) * (bitmapHeight + 1)];
int index = 0;
float ratio = (float) touchX / (float) width;
int alpha = 0;
for (int y = 0; y <= bitmapHeight; y++) {
float fy = height / bitmapHeight * y;
float longDisSide = touchY > height - touchY ? touchY : height - touchY;
float longRatio = Math.abs(fy - touchY) / longDisSide;
longRatio = interpolator.getInterpolation(longRatio);
float realWidth = longRatio * (touchX - delayOffsetX);
for (int x = 0; x <= bitmapWidth; x++) {
verts[index * 2 + 0] = (width * ratio - (realWidth)) / bitmapWidth * x;
float gap = 60.0F * (1.0F - ratio);
float realHeight = height - ((float) Math.sin((x * 2) * 0.5F) * gap + gap);
float offsetY = realHeight / bitmapHeight * y;
verts[index * 2 + 1] = (height - realHeight) / 2 + offsetY;
int px = (int) verts[index * 2 + 0];
int py = (int) verts[index * 2 + 1];
px = px < 0 ? 0 : px;
py = py < 0 ? 0 : py;
px = px > bitmap.getWidth() ? bitmap.getWidth() : px;
py = py > bitmap.getHeight() ? bitmap.getHeight() : py;
int color;
int channel = 255 - (int) (height - realHeight) * 2;
if (channel < 255) {
alpha = (int) ((255 - channel) / 120.0F * maxAlpha) * 4;
}
if (newApiFlag) {
channel = channel < 0 ? 0 : channel;
channel = channel > 255 ? 255 : channel;
color = 0xFF000000 | channel << 16 | channel << 8 | channel;
colors[index] = color;
}
index += 1;
}
}
canvas.drawBitmapMesh(bitmap, bitmapWidth, bitmapHeight, verts, 0, colors, 0, null);
if (!newApiFlag) {
alpha = alpha > 255 ? 255 : alpha;
alpha = alpha < 0 ? 0 : alpha;
paint.setAlpha(alpha);
canvas.drawBitmapMesh(shadowMask, bitmapWidth, bitmapHeight, verts, 0, null, 0, paint);
paint.setAlpha(255);
}
}
}
然后是布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.bitmapmesh.MainActivity" >
<com.example.bitmapmesh.BitmapMeshView
android:id="@+id/bitmapmesh"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
OK 附上下载地址
http://download.youkuaiyun.com/detail/u010107250/8433023