实现拖拽效果一 自定义带拖拽功能的GridView
因为demo适配了公司的业务,具体图层不好放出来,难受。但是这毕竟只是记录学习,自己知道实现了就好!
首先、既然是GridView实现拖拽效果,这肯定涉及事件分发,需要了解一下事件分发,这个可以去了解一下。我自己关于事件分发的学习也会发出来。
首先要实现拖拽功能的GridView,要关注几点
- 事件分发和消费,手指点击下去,down代表一个新的事件开始了,up和cancel表示事件结束了,千万不要忽略了cancel事件。
- 长按反馈,因为你长按开启拖动,所以好歹要让用户知道长按结束了,可以拖动了吧,可以通过震动和镜像变大实现。
- 创建镜像,这个是为了第二个服务的,但是这个是很重要的,要认真开,涉及windows的窗口。同时包括移除和创建和缓存。
- 屏幕适配。适当的行列数和间隔都是要考虑的。
- 滚动,因为如果开启了拖拽事件,gridView的滚动事件就报销了,需要我们自己来定义实现新的滚动事件了。
- 长按和滚动的线程开启。
- 持久化
废话不多说了,直接上源码和资源文件了,相关部分解释源码都有注解,还可以自己点击查看android源码更方便。
自定义的GridView
public class DragGridView extends GridView {
public static final String TAG = DragGridView.class.getSimpleName();
/**
* DragGridView的item长按响应的时间, 默认是1000毫秒,也可以自行设置
*/
private long dragResponseMS = 1000;
/**
* 是否可以拖拽,默认不可以
*/
private boolean isDrag = true;
private int mDownX;
private int mDownY;
private int moveX;
private int moveY;
/**
* 正在拖拽的position
*/
private int mDragPosition;
/**
* 刚开始拖拽的item对应的View
*/
private View mStartDragItemView = null;
/**
* 用于拖拽的镜像,这里直接用一个ImageView
*/
private ImageView mDragImageView;
/**
* 用于拖拽的镜像,这里直接放入拖动的 Image
*/
private FrameLayout mDragLayout;
/**
* 震动器
*/
private Vibrator mVibrator;
/**
* Windows窗口获取一个View,用于镜像
*/
private WindowManager mWindowManager;
/**
* item镜像的布局参数
*/
private WindowManager.LayoutParams mWindowLayoutParams;
/**
* 我们拖拽的item对应的Bitmap
*/
private Bitmap mDragBitmap;
/**
* 按下的点到所在item的上边缘的距离
*/
private int mPoint2ItemTop;
/**
* 按下的点到所在item的左边缘的距离
*/
private int mPoint2ItemLeft;
/**
* DragGridView距离屏幕顶部的偏移量
*/
private int mOffset2Top;
/**
* DragGridView距离屏幕左边的偏移量
*/
private int mOffset2Left;
/**
* 状态栏的高度
*/
private int mStatusHeight;
/**
* DragGridView自动向下滚动的边界值
*/
private int mDownScrollBorder;
/**
* DragGridView自动向上滚动的边界值
*/
private int mUpScrollBorder;
/**
* DragGridView自动滚动的速度
*/
private static final int speed = 20;
/**
* 交换动画
*/
private boolean mAnimationEnd = true;
/**
* 适配器
*/
private DragGridBaseAdapter mDragAdapter;
/**
* 行数列数
*/
private int mNumColumns;
private int mColumnWidth;
/**
* 是否设置
*/
private boolean mNumColumnsSet;
/**
* 间隔
*/
private int mHorizontalSpacing;
/**
* View的高度
*/
private int mViewHeight;
/**
* 拖动时景象放大倍数
*/
private float mDragScale = 1.2f;
/**
* 大小变化时间单位毫秒
*/
private int mScaleMill = 200;
/**
* 最后一个 position是否能够移动
*/
private boolean mDragLastPosition = false;
/**
* position 位置的可以开始拖动
*/
private int mDragStartPosition = 7;
/**
* 是否正在执行缩放动画
*/
private boolean mIsScaleAnima = false;
/**
* 是否震动
*/
private boolean mIsVibrator = false;
public DragGridView(Context context) {
this(context, null);
}
public DragGridView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DragGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
/**
* 自定义属性
*/
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.DragGridView);
mDragScale = typedArray.getFloat(R.styleable.DragGridView_drag_scale, 1.2f);
mScaleMill = typedArray.getInteger(R.styleable.DragGridView_scale_mill, 200);
mDragStartPosition = typedArray.getInteger(R.styleable.DragGridView_drag_start_position, 7);
mDragLastPosition = typedArray.getBoolean(R.styleable.DragGridView_drag_last_position, false);
mIsVibrator = typedArray.getBoolean(R.styleable.DragGridView_vibrator, false);
//回收自定义属性,因为自定义属性是通过池+单例实现的所有要释放回收
typedArray.recycle();
/**
* 自定义控件,这里防止可视化编译器无法识别。编辑模式下返回true
*/
if (!isInEditMode()) {
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mStatusHeight = getStatusHeight(context); // 获取状态栏的高度
}
if (!mNumColumnsSet) {
mNumColumns = AUTO_FIT; //尽可能填满可用空间
}
}
/**
* 设置间隔
*/
private void initView(){
int mSpacing = 10;
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
if (Build.MODEL.equals("N3")){
mSpacing = 7;
}
setHorizontalSpacing(mSpacing);
setVerticalSpacing(mSpacing);
setLayoutParams(params);
//初始化适配器
}
private Handler mHandler = new Handler();
// 用来处理是否为长按的Runnable
private Runnable mLongClickRunnable = new Runnable() {
@Override
public void run() {
isDrag = true; // 设置可以拖拽
if (mIsVibrator) mVibrator.vibrate(50); // 震动一下
mStartDragItemView.setVisibility(View.INVISIBLE);// 隐藏该item
// 根据我们按下的点显示item镜像
createDragImage(mDragBitmap, mDownX, mDownY);
}
};
/**
* 加载适配器
* @param adapter
*/
@Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
if (!isInEditMode()) {
if (adapter instanceof DragGridBaseAdapter) {
mDragAdapter = (DragGridBaseAdapter) adapter;
} else {
throw new IllegalStateException(
"the adapter must be implements DragGridAdapter");
}
}
}
/**
* 设置第几位可以开始拖动
* @param dragStartPosition
*/
public void setDragStartPosition(int dragStartPosition) {
mDragStartPosition = dragStartPosition;
}
@Override
public void setNumColumns(int numColumns) {
super.setNumColumns(numColumns);
mNumColumnsSet = true;
this.mNumColumns = numColumns;
}
@Override
public void setColumnWidth(int columnWidth) {
super.setColumnWidth(columnWidth);
mColumnWidth = columnWidth;
}
@Override
public void setHorizontalSpacing(int horizontalSpacing) {
super.setHorizontalSpacing(horizontalSpacing);
this.mHorizontalSpacing = horizontalSpacing;
}
/**
* 若设置为AUTO_FIT,计算有多少列
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mNumColumns == AUTO_FIT) {
int numFittedColumns;
if (mColumnWidth > 0) {
int gridWidth = Math.max(MeasureSpec.getSize(widthMeasureSpec)
- getPaddingLeft() - getPaddingRight(), 0);
numFittedColumns = gridWidth / mColumnWidth;
if (numFittedColumns > 0) {
while (numFittedColumns != 1) {
if (numFittedColumns * mColumnWidth
+ (numFittedColumns - 1) * mHorizontalSpacing > gridWidth) {
numFittedColumns--;
} else {
break;
}
}
} else {
numFittedColumns = 1;
}
} else {
numFittedColumns = 2;
}
mNumColumns = numFittedColumns;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mViewHeight = t ;
}
/**
* 设置响应拖拽的毫秒数,默认是1000毫秒
*
* @param dragResponseMS
*/
public void setDragResponseMS(long dragResponseMS) {
this.dragResponseMS = dragResponseMS;
}
/**
* 事件分发开始,这里是控制事件分发
* @param ev
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownX = (int) ev.getX();
mDownY = (int) ev.getY();
// 根据按下的X,Y坐标获取所点击item的position
mDragPosition = pointToPosition(mDownX, mDownY);
//如果是前mDragStartPosition不执行长按
if (mDragPosition < mDragStartPosition) {
return super.dispatchTouchEvent(ev);
}
//如果是最后一位不交换
// if (null != getAdapter() && mDragPosition == (getAdapter().getCount() - 1) && !mDragLastPosition) {
// return super.dispatchTouchEvent(ev);
// }
if (mDragPosition == AdapterView.INVALID_POSITION) {
return super.dispatchTouchEvent(ev);
}
// 使用Handler延迟dragResponseMS执行mLongClickRunnable
mHandler.postDelayed(mLongClickRunnable, dragResponseMS);
// 根据position获取该item所对应的View
mStartDragItemView = getChildAt(mDragPosition - getFirstVisiblePosition());
// 属性定义有解释 可以看一下Android的View坐标系了解一下
mPoint2ItemTop = mDownY - mStartDragItemView.getTop();
mPoint2ItemLeft = mDownX - mStartDragItemView.getLeft();
mOffset2Top = (int) (ev.getRawY() - mDownY);
mOffset2Left = (int) (ev.getRawX() - mDownX);
//启动拖拽事件,滚动事件会失效
// 获取DragGridView自动向上滚动的偏移量,小于这个值,DragGridView向下滚动
mDownScrollBorder = getHeight() / 5;
// 获取DragGridView自动向下滚动的偏移量,大于这个值,DragGridView向上滚动
mUpScrollBorder = getHeight() * 4 / 5;
// 开启mDragItemView绘图缓存
mStartDragItemView.setDrawingCacheEnabled(true);
// 获取mDragItemView在缓存中的Bitmap对象
Bitmap drawingCache = mStartDragItemView.getDrawingCache();
// mDragBitmap = Bitmap.createBitmap(drawingCache);
mDragBitmap = Bitmap.createScaledBitmap(drawingCache, (int) (drawingCache.getWidth() * mDragScale), (int) (drawingCache.getHeight() * mDragScale), true);
// 这一步很关键,释放绘图缓存,避免出现重复的镜像
mStartDragItemView.destroyDrawingCache();
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int) ev.getX();
int moveY = (int) ev.getY();
// 如果我们在按下的item上面移动,只要不超过item的边界我们就不移除mRunnable
if (!isTouchInItem(mStartDragItemView, moveX, moveY) || Math.abs(moveY - mDownY) > 100) {
mHandler.removeCallbacks(mLongClickRunnable);
}
break;
case MotionEvent.ACTION_UP: //这里注意UP和Cancel都要处理的,防止长按起来图标,然后没拖拽,就抬起手指,放弃了
case MotionEvent.ACTION_CANCEL:
mHandler.removeCallbacks(mLongClickRunnable);
mHandler.removeCallbacks(mScrollRunnable);
break;
}
return super.dispatchTouchEvent(ev);
}
/**
* 是否点击在GridView的item上面
*
* @param x
* @param y
* @return
*/
private boolean isTouchInItem(View dragView, int x, int y) {
if (dragView == null) {
return false;
}
int leftOffset = dragView.getLeft();
int topOffset = dragView.getTop();
if (x < leftOffset || x > leftOffset + dragView.getWidth()) {
return false;
}
if (y < topOffset || y > topOffset + dragView.getHeight()) {
return false;
}
return true;
}
/**
* 事件消费
* @param ev
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (isDrag && mDragImageView != null) {
switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE: //一旦down下来就是新的事件
moveX = (int) ev.getX();
moveY = (int) ev.getY();
// 拖动item
onDragItem(moveX, moveY);
break;
case MotionEvent.ACTION_UP: //与上面同理,Up和Cancel都要处理
case MotionEvent.ACTION_CANCEL:
onStopDrag((int) ev.getX(), (int) ev.getY());
isDrag = false;
break;
}
return true;
}
return super.onTouchEvent(ev);
}
/**
* 创建拖动的镜像
*
* @param bitmap
* @param downX 按下的点相对父控件的X坐标
* @param downY 按下的点相对父控件的X坐标
*/
private void createDragImage(Bitmap bitmap, int downX, int downY) {
mWindowLayoutParams = new WindowManager.LayoutParams();
mWindowLayoutParams.format = PixelFormat.TRANSLUCENT; // 图片之外的其他地方透明
mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
mWindowLayoutParams.x = (int) (downX - mPoint2ItemLeft * mDragScale + mOffset2Left);
mWindowLayoutParams.y = (int) (downY - mPoint2ItemTop * mDragScale + mOffset2Top - mStatusHeight);
mWindowLayoutParams.alpha = 1.0f; // 透明度
mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
mDragImageView = new ImageView(getContext());
mDragImageView.setImageBitmap(bitmap);
mDragImageView.setBackgroundResource(R.drawable.bg_shadow);
mDragLayout = new FrameLayout(getContext());
mDragLayout.addView(mDragImageView);
mWindowManager.addView(mDragLayout, mWindowLayoutParams);
}
/**
* 从界面上面移除拖动镜像
*/
private void removeDragImage() {
if (mDragImageView != null) {
mWindowManager.removeView(mDragLayout);
mDragLayout = null;
mDragImageView = null;
}
}
/**
* 拖动item,在里面实现了item镜像的位置更新,item的相互交换以及GridView的自行滚动
*/
private void onDragItem(int moveX, int moveY) {
mWindowLayoutParams.x = moveX - mPoint2ItemLeft + mOffset2Left;
mWindowLayoutParams.y = moveY - mPoint2ItemTop + mOffset2Top - mStatusHeight;
mWindowManager.updateViewLayout(mDragLayout, mWindowLayoutParams); // 更新镜像的位置
onSwapItem(moveX, moveY);
// GridView自动滚动
mHandler.post(mScrollRunnable);
//如果拖拽到删除区域删除
//这里也要自己根据View坐标系自己设定想要的移除位置 我这个是超过gridView高度,在他上面就删除
if (moveY < (mViewHeight)) {
if (mIsVibrator) mVibrator.vibrate(50);
//缩小 动画和震动效果
if (!mIsScaleAnima) { //是否执行效果
/**
* 补间动画 缩放动画 平移动画
* 想了解的可以自己了解一下
*/
ScaleAnimation scaleAnim = new ScaleAnimation(1.0f, .5f, 1.0f, .5f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
TranslateAnimation translateAnim = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.25f);
AnimationSet anim = new AnimationSet(false);
anim.addAnimation(scaleAnim);
anim.addAnimation(translateAnim);
anim.setDuration(mScaleMill);
anim.setFillAfter(true); //动画终止时停留在最后一帧 否则View会先回到原先位置再删除
mDragImageView.clearAnimation();
mDragImageView.startAnimation(anim);
mIsScaleAnima = true;
}
} else {
if (mIsScaleAnima) {
mDragImageView.clearAnimation();
mIsScaleAnima = false;
}
}
}
/**
* 当moveY的值大于向上滚动的边界值,触发GridView自动向上滚动 当moveY的值小于向下滚动的边界值,触发GridView自动向下滚动
* 否则不进行滚动
*/
private Runnable mScrollRunnable = new Runnable() {
@Override
public void run() {
int scrollY;
if (getFirstVisiblePosition() == 0 || getLastVisiblePosition() == getCount() - 1) {
mHandler.removeCallbacks(mScrollRunnable);
}
if (moveY > mUpScrollBorder) {
scrollY = speed;
mHandler.postDelayed(mScrollRunnable, 25);
} else if (moveY < mDownScrollBorder) {
scrollY = -speed;
mHandler.postDelayed(mScrollRunnable, 25);
} else {
scrollY = 0;
mHandler.removeCallbacks(mScrollRunnable);
}
smoothScrollBy(scrollY, 10);
}
};
/**
* 交换item,并且控制item之间的显示与隐藏效果
*
* @param moveX
* @param moveY
*/
private void onSwapItem(int moveX, int moveY) {
// 获取我们手指移动到的那个item的position
final int tempPosition = pointToPosition(moveX, moveY);
//如果是前mDragStartPosition位不交换
if (tempPosition < mDragStartPosition) {
return;
}
//如果是最后一位不交换
// if (null != getAdapter() && tempPosition == (getAdapter().getCount() - 1) && !mDragLastPosition) {
// return;
// }
// 假如tempPosition 改变了并且tempPosition不等于-1,则进行交换
if (tempPosition != mDragPosition
&& tempPosition != AdapterView.INVALID_POSITION
&& mAnimationEnd
) {
mDragAdapter.reorderItems(mDragPosition, tempPosition);
mDragAdapter.setHideItem(tempPosition);
final ViewTreeObserver observer = getViewTreeObserver();
observer.addOnPreDrawListener(new OnPreDrawListener() {
@Override
public boolean onPreDraw() {
observer.removeOnPreDrawListener(this);
animateReorder(mDragPosition, tempPosition);
mDragPosition = tempPosition;
return true;
}
});
}
}
/**
* 创建移动动画
*
* @param view
* @param startX
* @param endX
* @param startY
* @param endY
* @return
*/
private AnimatorSet createTranslationAnimations(View view, float startX, float endX, float startY, float endY) {
ObjectAnimator animX = ObjectAnimator.ofFloat(view, "translationX",
startX, endX);
ObjectAnimator animY = ObjectAnimator.ofFloat(view, "translationY",
startY, endY);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
return animSetXY;
}
/**
* item的交换动画效果
*
* @param oldPosition
* @param newPosition
*/
private void animateReorder(final int oldPosition, final int newPosition) {
boolean isForward = newPosition > oldPosition;
List<Animator> resultList = new LinkedList<Animator>();
if (isForward) {
for (int pos = oldPosition; pos < newPosition; pos++) {
View view = getChildAt(pos - getFirstVisiblePosition());
if (null == view) return;
if ((pos + 1) % mNumColumns == 0) {
resultList.add(createTranslationAnimations(view,
-view.getWidth() * (mNumColumns - 1), 0,
view.getHeight(), 0));
} else {
resultList.add(createTranslationAnimations(view,
view.getWidth(), 0, 0, 0));
}
}
} else {
for (int pos = oldPosition; pos > newPosition; pos--) {
View view = getChildAt(pos - getFirstVisiblePosition());
if (null == view) return;
if ((pos + mNumColumns) % mNumColumns == 0) {
resultList.add(createTranslationAnimations(view,
view.getWidth() * (mNumColumns - 1), 0,
-view.getHeight(), 0));
} else {
resultList.add(createTranslationAnimations(view,
-view.getWidth(), 0, 0, 0));
}
}
}
AnimatorSet resultSet = new AnimatorSet();
resultSet.playTogether(resultList);
resultSet.setDuration(300);
resultSet.setInterpolator(new AccelerateDecelerateInterpolator());
resultSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mAnimationEnd = false;
}
@Override
public void onAnimationEnd(Animator animation) {
mAnimationEnd = true;
}
});
resultSet.start();
}
/**
* 停止拖拽我们将之前隐藏的item显示出来,并将镜像移除
*/
private void onStopDrag(int moveX, int moveY) {
View view = getChildAt(mDragPosition - getFirstVisiblePosition());
if (view != null) {
view.setVisibility(View.VISIBLE);
}
mDragAdapter.setHideItem(-1);
removeDragImage();
if (moveY < (mViewHeight)) {
mIsScaleAnima = false;
mDragAdapter.deleteItem(mDragPosition);
}
}
/**
* 获取状态栏的高度
*
* @param context
* @return
*/
private static int getStatusHeight(Context context) {
int statusHeight = 0;
Rect localRect = new Rect();
((Activity) context).getWindow().getDecorView().getWindowVisibleDisplayFrame(localRect);
statusHeight = localRect.top;
if (0 == statusHeight) {
Class<?> localClass;
try {
localClass = Class.forName("com.android.internal.R$dimen");
Object localObject = localClass.newInstance();
int i5 = Integer.parseInt(localClass
.getField("status_bar_height").get(localObject)
.toString());
statusHeight = context.getResources().getDimensionPixelSize(i5);
} catch (Exception e) {
e.printStackTrace();
}
}
return statusHeight;
}
public interface DragGridBaseAdapter {
/**
* 重新排列数据
*
* @param oldPosition
* @param newPosition
*/
void reorderItems(int oldPosition, int newPosition);
/**
* 设置某个item隐藏
*
* @param hidePosition
*/
void setHideItem(int hidePosition);
/**
* 删除某个 item
*
* @param deletePosition
*/
void deleteItem(int deletePosition);
}
}
attrs.xml文件中的自定义属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DragGridView">
<!--拖拽时候放大缩小倍数-->
<attr name="drag_scale" format="float"/>
<!-- 缩放动画时间-->
<attr name="scale_mill" format="integer"/>
<!-- 可以开始拖动的 position-->
<attr name="drag_start_position" format="integer"/>
<!-- 最后一位是否可以拖动-->
<attr name="drag_last_position" format="boolean"/>
<!--是否振动-->
<attr name="vibrator" format="boolean"/>
</declare-styleable>
</resources>
具体调用:
/**
** 因为我这个activity是作底部弹出框使用的,部分设置是不一样的,只跟activity有关
**/
public class GetMoreActivity extends Activity implements View.OnClickListener {
private static final String TAG = "GetMoreActivity";
private ArrayList<ItemData> mData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_get_more);
getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
DragGridView dragGridView = findViewById(R.id.dragGridView);
LinearLayout linearLayout = findViewById(R.id.ll_getMore);
linearLayout.setOnClickListener(view -> Log.d(TAG, "onClick: "));
initData();
dragGridView.setAdapter(new MyAdapter(mData,this));
}
@Override
public boolean onTouchEvent(MotionEvent event){
finish();
return true;
}
@Override
public void onClick(View view) {
}
private class MyAdapter extends BaseAdapter implements DragGridView.DragGridBaseAdapter {
private ArrayList<ItemData> mList;
private Context mContext;
public int mHidePosition = -1;
public MyAdapter(ArrayList<ItemData> list,Context mContext) {
this.mList = list;
this.mContext = mContext;
}
@Override
public int getCount() {
return mList.size();
}
@Override
public ItemData getItem(int position) {
return mList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
MyHolder holder;
if (convertView == null) {
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_item, parent, false);
holder = MyHolder.create(convertView);
convertView.setTag(holder);
} else {
holder = (MyHolder) convertView.getTag();
}
Resources resources = mContext.getResources();
DisplayMetrics dm = resources.getDisplayMetrics();
// float density = dm.density;
int width = dm.widthPixels;
int height = dm.heightPixels;
int myWidth = width / 4;
int myHeight = height / 6;
convertView.getLayoutParams().width = myWidth;
convertView.getLayoutParams().height = myHeight;
holder.mTextView.setText(getItem(position).getResId());
holder.mImageView.setBackgroundResource(mData.get(position).getBitmap());
//隐藏被拖动的
if (position == mHidePosition) {
convertView.setVisibility(View.INVISIBLE);
} else {
convertView.setVisibility(View.VISIBLE);
}
return convertView;
}
@Override
public void reorderItems(int oldPosition, int newPosition) {
int temp = getItem(oldPosition).getResId();
int bitmap1 = getItem(oldPosition).getBitmap();
if (oldPosition < newPosition) {
for (int i = oldPosition; i < newPosition; i++) {
Collections.swap(mList, i, i + 1);
}
} else if (oldPosition > newPosition) {
for (int i = oldPosition; i > newPosition; i--) {
Collections.swap(mList, i, i - 1);
}
}
mList.set(newPosition,new ItemData(temp,bitmap1));
ACache.get(mContext).put("itemsMore",mList); //持久化
}
@Override
public void setHideItem(int hidePosition) {
mHidePosition = hidePosition;
notifyDataSetChanged();
}
@Override
public void deleteItem(int deletePosition) {
if (null != mList && deletePosition < mList.size()) {
ItemData tem = mList.get(deletePosition);
mList.remove(deletePosition);
notifyDataSetChanged();
ACache.get(mContext).put("itemsMore", mList);
ArrayList<ItemData> homeData = (ArrayList<ItemData>) ACache.get(GetMoreActivity.this).getAsObject("itemsHome");
if (homeData != null){
homeData.add(homeData.size() - 1 ,tem);
Log.d(TAG, "deleteItem: 这里面");
ACache.get(mContext).put("itemsHome", homeData);
}
}
}
}
private static class MyHolder {
public ImageView mImageView;
public TextView mTextView;
public MyHolder(ImageView imageView, TextView textView) {
this.mImageView = imageView;
this.mTextView = textView;
}
public static MyHolder create(View rootView) {
ImageView image = rootView.findViewById(R.id.iv_getMore);
TextView text = rootView.findViewById(R.id.tv_getMore);
return new MyHolder(image, text);
}
}
private void addSource(int resId, int bitmapId){
if (resId ==0 || bitmapId ==0)
return;
mData.add(new ItemData(resId,bitmapId));
}
/**
**这里大家缓存自己的定义的类型和数据就行了
*/
private void initData(){
mData = new ArrayList<>();
if (ACache.get(this).getAsObject("itemsMore") != null){
Log.d(TAG, "initData222: " + ACache.get(this).getAsObject("itemsMore").toString());
mData = (ArrayList<ItemData>) ACache.get(this).getAsObject("itemsMore");
}
else {
addSource(R.string.trans_order_pay_new, R.drawable.item_ordernew);
addSource(R.string.trans_ant_credit_pay, R.drawable.item_antcredit);
addSource(R.string.qrcode_trans, R.drawable.item_qrcode_trade);
addSource(R.string.trans_membersale, R.drawable.item_membersale);
addSource(R.string.trans_sale, R.drawable.item_sale);
addSource(R.string.trans_auth, R.drawable.item_auth);
addSource(R.string.trans_orderrefund, R.drawable.order_refund_icon);
addSource(R.string.trans_sale_void, R.drawable.item_void);
addSource(R.string.trans_refund, R.drawable.item_refund);
addSource(R.string.trans_login, R.drawable.item_login);
addSource(R.string.trans_query, R.drawable.item_quey_money);
addSource(R.string.trans_detail, R.drawable.item_detail);
addSource(R.string.trans_print, R.drawable.item_print);
addSource(R.string.logout_settle, R.drawable.item_settle);
addSource(R.string.trans_cashout, R.drawable.item_cash_out);
addSource(R.string.lock_terminal, R.drawable.item_lock);
addSource(R.string.version_info, R.drawable.item_version);
addSource(R.string.version_more, R.drawable.item_version);
if (mData != null){
ACache.get(this).put("itemsMore",mData);
Log.d(TAG, "initData222: "+ACache.get(this).getAsObject("itemsMore"));
}
}
}
}
activity的xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".GetMoreActivity">
<ImageView
android:layout_width="match_parent"
android:id="@+id/iv_getMore"
android:layout_height="160dp"/>
<androidx.cardview.widget.CardView
android:id="@+id/cv_demo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@color/colorPrimary"
app:cardElevation="18dp"
app:cardMaxElevation="18dp"
app:cardCornerRadius="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="@+id/ll_getMore"
>
<com.jlpay.changeitemdemo.Widget.DragGridView
android:id="@+id/dragGridView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@android:color/white"
android:cacheColorHint="@android:color/white"
android:gravity="center"
android:listSelector="@android:color/white"
android:numColumns="4"
app:drag_last_position="true"
app:drag_scale="1.05"
app:drag_start_position="0"
app:scale_mill="200"
app:vibrator="true"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
adapter对应的xml文件
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_getMore"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/tv_getMore"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="center_horizontal"
android:layout_marginBottom="20dp"
android:text="测试"
/>
</FrameLayout>
持久化
时ACache,轻量级的Android持久化,想了解的可以百度一下,还是很好用的,需要注意一点的是,如果是使用自定义的bean,那么ACache存储使用的就是序列化,这时候bean必须集成Serializable,否则存储不进去的。ACache集成的就一个java文件,放入工具类就行,代码如下:
public class ACache {
public static final int TIME_HOUR = 60 * 60;
public static final int TIME_DAY = TIME_HOUR * 24;
private static final int MAX_SIZE = 1000 * 1000 * 50; // 50 mb
private static final int MAX_COUNT = Integer.MAX_VALUE; // 不限制存放数据的数量
private static Map<String, ACache> mInstanceMap = new HashMap<String, ACache>();
private ACacheManager mCache;
public static ACache get(Context ctx) {
return get(ctx, "ACache");
}
public static ACache get(Context ctx, String cacheName) {
File f = new File(ctx.getCacheDir(), cacheName);
return get(f, MAX_SIZE, MAX_COUNT);
}
public static ACache get(File cacheDir) {
return get(cacheDir, MAX_SIZE, MAX_COUNT);
}
public static ACache get(Context ctx, long max_zise, int max_count) {
File f = new File(ctx.getCacheDir(), "ACache");
return get(f, max_zise, max_count);
}
public static ACache get(File cacheDir, long max_zise, int max_count) {
ACache manager = mInstanceMap.get(cacheDir.getAbsoluteFile() + myPid());
if (manager == null) {
manager = new ACache(cacheDir, max_zise, max_count);
mInstanceMap.put(cacheDir.getAbsolutePath() + myPid(), manager);
}
return manager;
}
private static String myPid() {
return "_" + android.os.Process.myPid();
}
private ACache(File cacheDir, long max_size, int max_count) {
if (!cacheDir.exists() && !cacheDir.mkdirs()) {
throw new RuntimeException("can't make dirs in "
+ cacheDir.getAbsolutePath());
}
mCache = new ACacheManager(cacheDir, max_size, max_count);
}
// =======================================
// ============ String数据 读写 ==============
// =======================================
/**
* 保存 String数据 到 缓存中
*
* @param key
* 保存的key
* @param value
* 保存的String数据
*/
public void put(String key, String value) {
File file = mCache.newFile(key);
BufferedWriter out = null;
try {
out = new BufferedWriter(new FileWriter(file), 1024);
out.write(value);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
mCache.put(file);
}
}
/**
* 保存 String数据 到 缓存中
*
* @param key
* 保存的key
* @param value
* 保存的String数据
* @param saveTime
* 保存的时间,单位:秒
*/
public void put(String key, String value, int saveTime) {
put(key, Utils.newStringWithDateInfo(saveTime, value));
}
/**
* 读取 String数据
*
* @param key
* @return String 数据
*/
public String getAsString(String key) {
File file = mCache.get(key);
if (!file.exists())
return null;
boolean removeFile = false;
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(file));
String readString = "";
String currentLine;
while ((currentLine = in.readLine()) != null) {
readString += currentLine;
}
if (!Utils.isDue(readString)) {
return Utils.clearDateInfo(readString);
} else {
removeFile = true;
return null;
}
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (removeFile)
remove(key);
}
}
// =======================================
// ============= JSONObject 数据 读写 ==============
// =======================================
/**
* 保存 JSONObject数据 到 缓存中
*
* @param key
* 保存的key
* @param value
* 保存的JSON数据
*/
public void put(String key, JSONObject value) {
put(key, value.toString());
}
/**
* 保存 JSONObject数据 到 缓存中
*
* @param key
* 保存的key
* @param value
* 保存的JSONObject数据
* @param saveTime
* 保存的时间,单位:秒
*/
public void put(String key, JSONObject value, int saveTime) {
put(key, value.toString(), saveTime);
}
/**
* 读取JSONObject数据
*
* @param key
* @return JSONObject数据
*/
public JSONObject getAsJSONObject(String key) {
String JSONString = getAsString(key);
try {
JSONObject obj = new JSONObject(JSONString);
return obj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// =======================================
// ============ JSONArray 数据 读写 =============
// =======================================
/**
* 保存 JSONArray数据 到 缓存中
*
* @param key
* 保存的key
* @param value
* 保存的JSONArray数据
*/
public void put(String key, JSONArray value) {
put(key, value.toString());
}
/**
* 保存 JSONArray数据 到 缓存中
*
* @param key
* 保存的key
* @param value
* 保存的JSONArray数据
* @param saveTime
* 保存的时间,单位:秒
*/
public void put(String key, JSONArray value, int saveTime) {
put(key, value.toString(), saveTime);
}
/**
* 读取JSONArray数据
*
* @param key
* @return JSONArray数据
*/
public JSONArray getAsJSONArray(String key) {
String JSONString = getAsString(key);
try {
JSONArray obj = new JSONArray(JSONString);
return obj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// =======================================
// ============== byte 数据 读写 =============
// =======================================
/**
* 保存 byte数据 到 缓存中
*
* @param key
* 保存的key
* @param value
* 保存的数据
*/
public void put(String key, byte[] value) {
File file = mCache.newFile(key);
FileOutputStream out = null;
try {
out = new FileOutputStream(file);
out.write(value);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
mCache.put(file);
}
}
/**
* 保存 byte数据 到 缓存中
*
* @param key
* 保存的key
* @param value
* 保存的数据
* @param saveTime
* 保存的时间,单位:秒
*/
public void put(String key, byte[] value, int saveTime) {
put(key, Utils.newByteArrayWithDateInfo(saveTime, value));
}
/**
* 获取 byte 数据
*
* @param key
* @return byte 数据
*/
public byte[] getAsBinary(String key) {
RandomAccessFile RAFile = null;
boolean removeFile = false;
try {
File file = mCache.get(key);
if (!file.exists())
return null;
RAFile = new RandomAccessFile(file, "r");
byte[] byteArray = new byte[(int) RAFile.length()];
RAFile.read(byteArray);
if (!Utils.isDue(byteArray)) {
return Utils.clearDateInfo(byteArray);
} else {
removeFile = true;
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (RAFile != null) {
try {
RAFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (removeFile)
remove(key);
}
}
// =======================================
// ============= 序列化 数据 读写 ===============
// =======================================
/**
* 保存 Serializable数据 到 缓存中
*
* @param key
* 保存的key
* @param value
* 保存的value
*/
public void put(String key, Serializable value) {
put(key, value, -1);
}
/**
* 保存 Serializable数据到 缓存中
*
* @param key
* 保存的key
* @param value
* 保存的value
* @param saveTime
* 保存的时间,单位:秒
*/
public void put(String key, Serializable value, int saveTime) {
ByteArrayOutputStream baos = null;
ObjectOutputStream oos = null;
try {
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(value);
byte[] data = baos.toByteArray();
if (saveTime != -1) {
put(key, data, saveTime);
} else {
put(key, data);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
oos.close();
} catch (IOException e) {
}
}
}
/**
* 读取 Serializable数据
*
* @param key
* @return Serializable 数据
*/
public Object getAsObject(String key) {
byte[] data = getAsBinary(key);
if (data != null) {
ByteArrayInputStream bais = null;
ObjectInputStream ois = null;
try {
bais = new ByteArrayInputStream(data);
ois = new ObjectInputStream(bais);
Object reObject = ois.readObject();
return reObject;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
if (bais != null)
bais.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (ois != null)
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
// =======================================
// ============== bitmap 数据 读写 =============
// =======================================
/**
* 保存 bitmap 到 缓存中
*
* @param key
* 保存的key
* @param value
* 保存的bitmap数据
*/
public void put(String key, Bitmap value) {
put(key, Utils.Bitmap2Bytes(value));
}
/**
* 保存 bitmap 到 缓存中
*
* @param key
* 保存的key
* @param value
* 保存的 bitmap 数据
* @param saveTime
* 保存的时间,单位:秒
*/
public void put(String key, Bitmap value, int saveTime) {
put(key, Utils.Bitmap2Bytes(value), saveTime);
}
/**
* 读取 bitmap 数据
*
* @param key
* @return bitmap 数据
*/
public Bitmap getAsBitmap(String key) {
if (getAsBinary(key) == null) {
return null;
}
return Utils.Bytes2Bimap(getAsBinary(key));
}
// =======================================
// ============= drawable 数据 读写 =============
// =======================================
/**
* 保存 drawable 到 缓存中
*
* @param key
* 保存的key
* @param value
* 保存的drawable数据
*/
public void put(String key, Drawable value) {
put(key, Utils.drawable2Bitmap(value));
}
/**
* 保存 drawable 到 缓存中
*
* @param key
* 保存的key
* @param value
* 保存的 drawable 数据
* @param saveTime
* 保存的时间,单位:秒
*/
public void put(String key, Drawable value, int saveTime) {
put(key, Utils.drawable2Bitmap(value), saveTime);
}
/**
* 读取 Drawable 数据
*
* @param key
* @return Drawable 数据
*/
public Drawable getAsDrawable(String key) {
if (getAsBinary(key) == null) {
return null;
}
return Utils.bitmap2Drawable(Utils.Bytes2Bimap(getAsBinary(key)));
}
/**
* 获取缓存文件
*
* @param key
* @return value 缓存的文件
*/
public File file(String key) {
File f = mCache.newFile(key);
if (f.exists())
return f;
return null;
}
/**
* 移除某个key
*
* @param key
* @return 是否移除成功
*/
public boolean remove(String key) {
return mCache.remove(key);
}
/**
* 清除所有数据
*/
public void clear() {
mCache.clear();
}
/**
* @title 缓存管理器
* @version 1.0
*/
public class ACacheManager {
private final AtomicLong cacheSize;
private final AtomicInteger cacheCount;
private final long sizeLimit;
private final int countLimit;
private final Map<File, Long> lastUsageDates = Collections
.synchronizedMap(new HashMap<File, Long>());
protected File cacheDir;
private ACacheManager(File cacheDir, long sizeLimit, int countLimit) {
this.cacheDir = cacheDir;
this.sizeLimit = sizeLimit;
this.countLimit = countLimit;
cacheSize = new AtomicLong();
cacheCount = new AtomicInteger();
calculateCacheSizeAndCacheCount();
}
/**
* 计算 cacheSize和cacheCount
*/
private void calculateCacheSizeAndCacheCount() {
new Thread(new Runnable() {
@Override
public void run() {
int size = 0;
int count = 0;
File[] cachedFiles = cacheDir.listFiles();
if (cachedFiles != null) {
for (File cachedFile : cachedFiles) {
size += calculateSize(cachedFile);
count += 1;
lastUsageDates.put(cachedFile,
cachedFile.lastModified());
}
cacheSize.set(size);
cacheCount.set(count);
}
}
}).start();
}
private void put(File file) {
int curCacheCount = cacheCount.get();
while (curCacheCount + 1 > countLimit) {
long freedSize = removeNext();
cacheSize.addAndGet(-freedSize);
curCacheCount = cacheCount.addAndGet(-1);
}
cacheCount.addAndGet(1);
long valueSize = calculateSize(file);
long curCacheSize = cacheSize.get();
while (curCacheSize + valueSize > sizeLimit) {
long freedSize = removeNext();
curCacheSize = cacheSize.addAndGet(-freedSize);
}
cacheSize.addAndGet(valueSize);
Long currentTime = System.currentTimeMillis();
file.setLastModified(currentTime);
lastUsageDates.put(file, currentTime);
}
private File get(String key) {
File file = newFile(key);
Long currentTime = System.currentTimeMillis();
file.setLastModified(currentTime);
lastUsageDates.put(file, currentTime);
return file;
}
private File newFile(String key) {
return new File(cacheDir, key.hashCode() + "");
}
private boolean remove(String key) {
File image = get(key);
return image.delete();
}
private void clear() {
lastUsageDates.clear();
cacheSize.set(0);
File[] files = cacheDir.listFiles();
if (files != null) {
for (File f : files) {
f.delete();
}
}
}
/**
* 移除旧的文件
*
* @return
*/
private long removeNext() {
if (lastUsageDates.isEmpty()) {
return 0;
}
Long oldestUsage = null;
File mostLongUsedFile = null;
Set<Entry<File, Long>> entries = lastUsageDates.entrySet();
synchronized (lastUsageDates) {
for (Entry<File, Long> entry : entries) {
if (mostLongUsedFile == null) {
mostLongUsedFile = entry.getKey();
oldestUsage = entry.getValue();
} else {
Long lastValueUsage = entry.getValue();
if (lastValueUsage < oldestUsage) {
oldestUsage = lastValueUsage;
mostLongUsedFile = entry.getKey();
}
}
}
}
long fileSize = calculateSize(mostLongUsedFile);
if (mostLongUsedFile.delete()) {
lastUsageDates.remove(mostLongUsedFile);
}
return fileSize;
}
private long calculateSize(File file) {
return file.length();
}
}
/**
* @title 时间计算工具类
* @version 1.0
*/
private static class Utils {
/**
* 判断缓存的String数据是否到期
*
* @param str
* @return true:到期了 false:还没有到期
*/
private static boolean isDue(String str) {
return isDue(str.getBytes());
}
/**
* 判断缓存的byte数据是否到期
*
* @param data
* @return true:到期了 false:还没有到期
*/
private static boolean isDue(byte[] data) {
String[] strs = getDateInfoFromDate(data);
if (strs != null && strs.length == 2) {
String saveTimeStr = strs[0];
while (saveTimeStr.startsWith("0")) {
saveTimeStr = saveTimeStr
.substring(1, saveTimeStr.length());
}
long saveTime = Long.valueOf(saveTimeStr);
long deleteAfter = Long.valueOf(strs[1]);
if (System.currentTimeMillis() > saveTime + deleteAfter * 1000) {
return true;
}
}
return false;
}
private static String newStringWithDateInfo(int second, String strInfo) {
return createDateInfo(second) + strInfo;
}
private static byte[] newByteArrayWithDateInfo(int second, byte[] data2) {
byte[] data1 = createDateInfo(second).getBytes();
byte[] retdata = new byte[data1.length + data2.length];
System.arraycopy(data1, 0, retdata, 0, data1.length);
System.arraycopy(data2, 0, retdata, data1.length, data2.length);
return retdata;
}
private static String clearDateInfo(String strInfo) {
if (strInfo != null && hasDateInfo(strInfo.getBytes())) {
strInfo = strInfo.substring(strInfo.indexOf(mSeparator) + 1,
strInfo.length());
}
return strInfo;
}
private static byte[] clearDateInfo(byte[] data) {
if (hasDateInfo(data)) {
return copyOfRange(data, indexOf(data, mSeparator) + 1,
data.length);
}
return data;
}
private static boolean hasDateInfo(byte[] data) {
return data != null && data.length > 15 && data[13] == '-'
&& indexOf(data, mSeparator) > 14;
}
private static String[] getDateInfoFromDate(byte[] data) {
if (hasDateInfo(data)) {
String saveDate = new String(copyOfRange(data, 0, 13));
String deleteAfter = new String(copyOfRange(data, 14,
indexOf(data, mSeparator)));
return new String[] { saveDate, deleteAfter };
}
return null;
}
private static int indexOf(byte[] data, char c) {
for (int i = 0; i < data.length; i++) {
if (data[i] == c) {
return i;
}
}
return -1;
}
private static byte[] copyOfRange(byte[] original, int from, int to) {
int newLength = to - from;
if (newLength < 0)
throw new IllegalArgumentException(from + " > " + to);
byte[] copy = new byte[newLength];
System.arraycopy(original, from, copy, 0,
Math.min(original.length - from, newLength));
return copy;
}
private static final char mSeparator = ' ';
private static String createDateInfo(int second) {
String currentTime = System.currentTimeMillis() + "";
while (currentTime.length() < 13) {
currentTime = "0" + currentTime;
}
return currentTime + "-" + second + mSeparator;
}
/*
* Bitmap → byte[]
*/
private static byte[] Bitmap2Bytes(Bitmap bm) {
if (bm == null) {
return null;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
return baos.toByteArray();
}
/*
* byte[] → Bitmap
*/
private static Bitmap Bytes2Bimap(byte[] b) {
if (b.length == 0) {
return null;
}
return BitmapFactory.decodeByteArray(b, 0, b.length);
}
/*
* Drawable → Bitmap
*/
private static Bitmap drawable2Bitmap(Drawable drawable) {
if (drawable == null) {
return null;
}
// 取 drawable 的长宽
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
// 取 drawable 的颜色格式
Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
: Bitmap.Config.RGB_565;
// 建立对应 bitmap
Bitmap bitmap = Bitmap.createBitmap(w, h, config);
// 建立对应 bitmap 的画布
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, w, h);
// 把 drawable 内容画到画布中
drawable.draw(canvas);
return bitmap;
}
/*
* Bitmap → Drawable
*/
@SuppressWarnings("deprecation")
private static Drawable bitmap2Drawable(Bitmap bm) {
if (bm == null) {
return null;
}
return new BitmapDrawable(bm);
}
}
}
bean类
如果实现想知道我自定义的数据类型,也可以去看实现二的方式,里面有,其实我比较推荐实现二,方便简单。
那么拜拜了,记录自己的学习,也与各位大佬们分享自己的学习。