Drawable:
Drawable就是一个可画的对象,表示一种可以在Canvas上进行绘制的抽象的概念,其可能是一张(BitmapDrawable),也可能是一个图形(ShapeDrawable),还有可能是一个图层(LayerDrawable),我们根据画图的需求,创建相应的可画对象,就可以将这个可画对象当作一块“画布(Canvas)”,在其上面操作可画对象,并最终将这种可画对象显示在画布上,有点类似于“内存画布“。
HorizontalScrollView+Drawable 实现案列:
自定义drawable
大体思路:
1>在HorizontalScrollView的onTouch()中监听ACTION_MOVE事件
2>getScrollX()得到HorizontalScrollView滑动距离找到两张渐变的图片的下标
3>HorizontalScrollView中设置图片的level 会在Drawable的onLevelChange()方法中监听到level变化
4>level变化 调用invalidateSelf()重新绘制 调用Drawable自身draw()
5>最后抠图绘制到屏幕上 抠图api Gravity.apply(......)
//从一个已有的bounds矩形边界范围中抠出一个矩形r
Gravity.apply(
gravity,//从左边还是右边开始抠
w,//目标矩形的宽
h, //目标矩形的高
bounds, //被抠出来的rect
r);//目标rect
canvas.save();//保存画布
canvas.clipRect(r);//切割
mUnselectedDrawable.draw(canvas);//画
canvas.restore();//恢复之前保存的画布
完整代码:
RevealDrawable.java
package com.dn_alan.myapplication;
import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.Gravity;
public class RevealDrawable extends Drawable {
private static final String TAG = "Reveal";
private final Rect mTmpRect = new Rect();
private Drawable mUnselectedDrawable;
private Drawable mSelectedDrawable;
private int mOrientation;
public static final int HORIZONTAL = 1;
public static final int VERTICAL = 2;
public RevealDrawable(Drawable unselected, Drawable selected, int orientation) {
mUnselectedDrawable = unselected;
mSelectedDrawable = selected;
mOrientation = orientation;
}
@Override
public void draw(Canvas canvas) {
// 绘制
int level = getLevel();//from 0 (minimum) to 10000
//三个区间
//右边区间和左边区间--设置成灰色
if (level == 10000 || level == 0) {
mUnselectedDrawable.draw(canvas);
} else if (level == 5000) {//全部选中--设置成彩色
mSelectedDrawable.draw(canvas);
} else {
//混合效果的Drawable
/**
* 将画板切割成两块-左边和右边
*/
final Rect r = mTmpRect;
//得到当前自身Drawable的矩形区域
Rect bounds = getBounds();
{
//1.先绘制灰色部分
//level 0~5000~10000
//比例
//4680 / 5000 -1f
float ratio = (level / 5000f) - 1f;
int w = bounds.width();
if (mOrientation == HORIZONTAL) {
//我们要扣的宽度
w = (int) (w * Math.abs(ratio));
}
int h = bounds.height();
if (mOrientation == VERTICAL) {
h = (int) (h * Math.abs(ratio));
}
int gravity = ratio < 0 ? Gravity.LEFT : Gravity.RIGHT;
//从一个已有的bounds矩形边界范围中抠出一个矩形r
Gravity.apply(
gravity,//从左边还是右边开始抠
w,//目标矩形的宽
h, //目标矩形的高
bounds, //被抠出来的rect
r);//目标rect
canvas.save();//保存画布
canvas.clipRect(r);//切割
mUnselectedDrawable.draw(canvas);//画
canvas.restore();//恢复之前保存的画布
}
{
//2.再绘制彩色部分
//level 0~5000~10000
//比例
float ratio = (level / 5000f) - 1f;
int w = bounds.width();
if (mOrientation == HORIZONTAL) {
w -= (int) (w * Math.abs(ratio));
}
int h = bounds.height();
if (mOrientation == VERTICAL) {
h -= (int) (h * Math.abs(ratio));
}
int gravity = ratio < 0 ? Gravity.RIGHT : Gravity.LEFT;
//从一个已有的bounds矩形边界范围中抠出一个矩形r
Gravity.apply(
gravity,//从左边还是右边开始抠
w,//目标矩形的宽
h, //目标矩形的高
bounds, //被抠出来的rect
r);//目标rect
canvas.save();//保存画布
canvas.clipRect(r);//切割
mSelectedDrawable.draw(canvas);//画
canvas.restore();//恢复之前保存的画布
}
}
}
@Override
protected void onBoundsChange(Rect bounds) {
// 定好两个Drawable图片的宽高---边界bounds
mUnselectedDrawable.setBounds(bounds);
mSelectedDrawable.setBounds(bounds);
Log.d(TAG, "w = " + bounds.width());
}
@Override
public int getIntrinsicWidth() {
//得到Drawable的实际宽度
return Math.max(mSelectedDrawable.getIntrinsicWidth(),
mUnselectedDrawable.getIntrinsicWidth());
}
@Override
public int getIntrinsicHeight() {
//得到Drawable的实际高度
return Math.max(mSelectedDrawable.getIntrinsicHeight(),
mUnselectedDrawable.getIntrinsicHeight());
}
@Override
protected boolean onLevelChange(int level) {
// 当设置level的时候回调---提醒自己重新绘制
invalidateSelf();
return true;
}
@Override
public void setAlpha(int i) {
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
}
@SuppressLint("WrongConstant")
@Override
public int getOpacity() {
return 0;
}
}
GallaryHorizonalScrollView.java
package com.dn_alan.myapplication;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
public class GallaryHorizonalScrollView extends HorizontalScrollView implements OnTouchListener {
private static final String TAG = "scrollView";
private LinearLayout container;
private int centerX;
private int icon_width;
public GallaryHorizonalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public GallaryHorizonalScrollView(Context context) {
super(context);
init();
}
private void init() {
//在ScrollView里面放置一个水平线性布局,再往里面放置很多ImageView
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
container = new LinearLayout(getContext());
container.setLayoutParams(params);
setOnTouchListener(this);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
//得到某一张图片的宽度
View v = container.getChildAt(0);
icon_width = v.getWidth();
Log.d(TAG, "icon_width = " + icon_width);
//得到hzv的中间x坐标
centerX = getWidth()/2;
Log.d(TAG, "centerX = " + centerX);
//处理下,中心坐标改为中心图片的左边界
centerX = centerX - icon_width/2;
//给LinearLayout和hzv之间设置边框距离
container.setPadding(centerX, 0, centerX, 0);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction()== MotionEvent.ACTION_MOVE){
//渐变图片
reveal();
}
return false;
}
private void reveal() {
// 渐变效果
//得到hzv滑出去的距离
int scrollX = getScrollX();
Log.d(TAG, scrollX + "");
//找到两张渐变的图片的下标--左,右
int index_left = scrollX/icon_width;
Log.d(TAG, index_left + "index_left");
int index_right = index_left + 1;
//设置图片的level
for (int i = 0; i < container.getChildCount(); i++) {
if(i==index_left||i==index_right){
//变化
//比例:
float ratio = 5000f/icon_width;
ImageView iv_left = (ImageView) container.getChildAt(index_left);
//scrollX%icon_width:代表滑出去的距离
//滑出去了icon_width/2 icon_width/2%icon_width
iv_left.setImageLevel(
//代表的是,我滑动之后的距离在5000份当中的份额
(int)(5000-scrollX%icon_width*ratio)
);
//右边
if(index_right<container.getChildCount()){
ImageView iv_right = (ImageView) container.getChildAt(index_right);
//scrollX%icon_width:代表滑出去的距离
//滑出去了icon_width/2 icon_width/2%icon_width
iv_right.setImageLevel(
(int)(10000-scrollX%icon_width*ratio)
);
}
}else{
//灰色
ImageView iv = (ImageView) container.getChildAt(i);
iv.setImageLevel(0);
}
}
}
//添加图片的方法
public void addImageViews(Drawable[] revealDrawables){
for (int i = 0; i < revealDrawables.length; i++) {
ImageView img = new ImageView(getContext());
img.setImageDrawable(revealDrawables[i]);
container.addView(img);
if(i==0){
img.setImageLevel(5000);
}
}
addView(container);
}
}
MainActivity.java
package com.dn_alan.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity {
private ImageView iv;
private int[] mImgIds = new int[]{ //7个
R.drawable.avft,
R.drawable.box_stack,
R.drawable.bubble_frame,
R.drawable.bubbles,
R.drawable.bullseye,
R.drawable.circle_filled,
R.drawable.circle_outline,
R.drawable.avft,
R.drawable.box_stack,
R.drawable.bubble_frame,
R.drawable.bubbles,
R.drawable.bullseye,
R.drawable.circle_filled,
R.drawable.circle_outline
};
private int[] mImgIds_active = new int[]{
R.drawable.avft_active, R.drawable.box_stack_active, R.drawable.bubble_frame_active,
R.drawable.bubbles_active, R.drawable.bullseye_active, R.drawable.circle_filled_active,
R.drawable.circle_outline_active,
R.drawable.avft_active, R.drawable.box_stack_active, R.drawable.bubble_frame_active,
R.drawable.bubbles_active, R.drawable.bullseye_active, R.drawable.circle_filled_active,
R.drawable.circle_outline_active
};
public Drawable[] revealDrawables;
protected int level = 5000;
private GallaryHorizonalScrollView hzv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
}
private void initView() {
hzv = (GallaryHorizonalScrollView) findViewById(R.id.hsv);
}
private void initData() {
revealDrawables = new Drawable[mImgIds.length];
for (int i = 0; i < mImgIds.length; i++) {
RevealDrawable rd = new RevealDrawable(
getResources().getDrawable(mImgIds[i]),
getResources().getDrawable(mImgIds_active[i]),
RevealDrawable.HORIZONTAL);
revealDrawables[i] = rd;
}
hzv.addImageViews(revealDrawables);
}
}