临文乍了了,彻卷兀若无。这篇文章主要讲述Android评论图片可移动顺序选择器相关的知识,希望能为你提供帮助。
岁月如梭好久没写了, 现在在广州一家公司实习了, 来了一个月了, 实习生没什么事干, 看到公司一个项目。android和ios的做的不一样( ios做这个项目的人多, 额不解释。。原来做这个玩意的也跳槽了) , 既ios的做的控件更酷炫, 我闲着没事, 把其中的一个控件和IOS做的差不多了, 来看看效果吧
文章图片
文章图片
【Android评论图片可移动顺序选择器】截的GIF图看上去有点快了, 因为CSDN上传图片不能超过两M所以帧有点大, 实际效果是正常的。好了, 先让我们看看不能移动交换顺序之前是怎么实现的吧。
文章图片
package com.test.jiupin.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
/**
* Created by liaoyalong on 2016/12/8.
*/public class AddImageGridView extends FrameLayout{private int width =
0;
//图片宽
private int height =
0;
//图片高
private int space =
dp2px(10);
//图片之间间隙
private int childCount =
0;
//孩子数public AddImageGridView(Context context) {
this(context,null);
}public AddImageGridView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}public AddImageGridView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}private void init() { //额没用上}public int getmSpace(){
return space;
}public int getmWidth() {
return width;
}public int getmHeight() {//这里我设置了宽高一样 ,
所以也没用上
return height;
}publicvoid addCammary(View view){//添加相机 ,
是第一个孩子
addView(view,0);
}public void addView(View view){//添加子vie后控件会自动重新测量布局
childCount =
getChildCount();
if(childCount =
=
5){//最多能添加5张图片,
既当添加到第五张图片的时候把相机删了
removeViewAt(0);
addView(view,4);
} else{
addView(view,childCount);
}}@
Override//最关键的了
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {childCount =
getChildCount();
int wdWidth =
MeasureSpec.getSize(widthMeasureSpec);
width =
(wdWidth - 3 * space) / 4;
//屏幕分为三份间隙和四份图片的宽度
space +
=
wdWidth % 4 / 3;
//重新计算间隙
height =
width;
//高度和宽度一样
int childWidthSPEC =
MeasureSpec.makeMeasureSpec(width,MeasureSpec.EXACTLY);
//精确测量
int childHeightSPEC =
MeasureSpec.makeMeasureSpec(height,MeasureSpec.EXACTLY);
for (int i =
0;
i <
childCount;
i+
+
) {
View view =
getChildAt(i);
view.measure(childWidthSPEC,childHeightSPEC);
}
int wdHeight =
height;
if(childCount >
4){
wdHeight +
=
height +
space;
//最多有五个既超过四个,
高度变成两层
}
setMeasuredDimension(wdWidth,wdHeight);
}@
Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
for (int i =
0;
i <
childCount;
i+
+
) {//上面我们自己测量好了,
这里我们自己放控件的位置,
不需要管原来left,top之类的值
View view =
getChildAt(i);
left =
0;
top =
0;
if(i >
0){
left =
i * width +
i * space;
//每个孩子对应的位置,
自己画图分析
}if(i =
=
4){//第二层
left =
0;
top =
height +
space;
}
right =
left +
width;
bottom =
top +
height;
view.layout(left,top,right,bottom);
}
}public int dp2px(int dp){return (int) (getResources().getDisplayMetrics().density * dp +
.5);
}
}
才一百行不到, 很容易看懂吧, 如果对测量不懂的, 可以看我以前写的自定义控件, 里面的。这就可以了, 实现了添加那种不能移动图片的控件。现在开始来做可以移动的吧, 我是这样做的。当按图片超过一秒的时候, 让这个图片隐藏, 然后用WindowManager添加一个可以自定义moveView来显示这个图片的BitMap, 并把图片的touch事件也交给添加的moveView来处理, 然后通过moveView的移动来判断需不需要与其它图片交换位置.moveView就像我以前的放腾讯拖到小球那样, 来看看具体的吧。
package com.test.jiupin.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.View;
/**
* Created by liaoyalong on 2016/12/12.
*/public class moveView extends View{private Bitmap mBitmap;
//按1秒钟图片的bitmap 既原显示的都是imageview 我把它们显示的bitmap 用iv.set(bitmap)
private int left;
//相对屏幕的位置,
画的时候要用
private int top;
//同上理
private int width;
//点击的图片的宽度
private int height;
private int mStatusBarheight;
//手机屏幕状态栏高度
private int wdWidth;
//屏幕宽度
private int wdHeight;
//屏幕高度
private int orgTop;
//刚点击时 点击图片相对屏幕的高度public moveView(Context context,View v,MotionEvent event,boolean is4,int spac) {
super(context);
mBitmap =
(Bitmap) v.getTag();
//从点击的图片那获取bitmap
left =
(int) (event.getRawX() - event.getX());
//相对屏幕像素 - 相对控件像素
top =
(int) (event.getRawY() - event.getY());
orgTop =
top;
width =
v.getWidth();
height =
v.getHeight();
if (is4){//第二层
orgTop =
orgTop - height - spac;
}
mStatusBarheight =
getStatusBarHeight();
//状态栏高度
wdWidth =
getResources().getDisplayMetrics().widthPixels;
//
wdHeight =
getResources().getDisplayMetrics().heightPixels;
}@
Override
protected void onDraw(Canvas canvas) {canvas.save();
canvas.translate(0,-mStatusBarheight);
//移状态栏高度,
这里不懂看我前面QQ移动小球的文章if(left <
0){
left =
0;
}else if(left +
width >
wdWidth){//不能移出屏幕
left =
wdWidth - width;
}
if (top <
mStatusBarheight){
top =
mStatusBarheight;
}else if(top +
height >
wdHeight){
top =
wdHeight - height;
}
canvas.drawBitmap(mBitmap,null,new Rect(left,top,left+
width,top+
height),null);
canvas.restore();
}@
Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){case MotionEvent.ACTION_DOWN:break;
case MotionEvent.ACTION_MOVE:left =
(int) (event.getRawX()-width/2);
//手指点的为中心点
top =
(int) (event.getRawY()-height/2);
if (mOnDragListener !=
null){//回调方法,
返回中心点与其它图片进行比较
mOnDragListener.onMove((int)event.getRawX(),(int)event.getRawY() +
mStatusBarheight - orgTop - height/2);
}
postInvalidate();
break;
case MotionEvent.ACTION_UP:if (mOnDragListener !=
null){//手抬起时触发消失回调
mOnDragListener.onDisappear();
}
break;
}
return true;
}public int getStatusBarHeight() {//获取状态栏高度
int result =
0;
int resourceId =
getResources().getIdentifier("
status_bar_height"
, "
dimen"
, "
android"
);
if (resourceId >
0) {
result =
getResources().getDimensionPixelSize(resourceId);
}
return result;
}public interface OnDragListener{void onDisappear();
void onMove(int centerX,int centerY);
}
private OnDragListener mOnDragListener;
public void setOnDragListener(OnDragListener listener){
mOnDragListener =
listener;
}
}
恩, 注释写的很相许了吧, 这里控件搞好了, 来activity中看看具体的控制问题吧, 首先先看看比较函数
/**
* 该不该交换图片
*
* @
param wx移动传回来的中心点
* @
param wy
* @
param x与之相比较的点
* @
param y
* @
param range 比较范围
* @
return
*/
private boolean shouldReplace(int wx, int wy, int x, int y, int range) {
boolean flag =
false;
if (wx >
x - range &
&
wx <
x +
range &
&
wy >
y - range &
&
wy <
y +
range) {
flag =
true;
}return flag;
}
确定了比较函数就是, 我们还要想改怎么进行比较, 我的思路是, 前面添加view的既AddImageGridView这个控件, 它添加一个非相机子view, 我就把它存入一个List,然后给list中存的所有view设置touch事件, 以一个int 型的CurrentSpace记录当前被长按1秒钟以上的view 在list的位置, 并Bitmap型的firSelect 记录被长按图片的bitmap,然后让它隐藏, 当需要交换的时候, 假设交换的是j 我把CurrentSpace对应的view设置bitmap为j的bitmap,并让j把当前显示的bitmap setTag().再让j对应的view隐藏,并把CurrentSpace设置为j。额。。。很难看懂我在说什么是吧, 我自己也觉得好难说清, 来看代码吧, 我尽量把注释标注详细, 等下我也会把完整代码下载链接放在评论区。
private void initMoveListener() {
for (int i =
0;
i <
mViews.size();
i+
+
) {//mViews存的那些评论图片final int finalI =
i;
mViews.get(i).setOnTouchListener(new View.OnTouchListener() {
@
Override
public boolean onTouch(final View v, final MotionEvent event) {if (MotionEvent.ACTION_DOWN =
=
event.getAction()) {
downTime =
System.currentTimeMillis();
//按下时的时间v.getParent().requestDisallowInterceptTouchEvent(true);
//请求父控件不要拦截触摸事件} else {
spacTime =
System.currentTimeMillis() - downTime;
//按了多久
}
//Log.e("
test"
,spacTime+
"
"
);
if (!first &
&
spacTime >
1000) {//按了一秒才让移动
curentSpace =
finalI;
//当前按得位置
first =
true;
v.setVisibility(View.INVISIBLE);
//把按得图片隐藏
if (mViews.size() <
5) {//创建移动图片
if (finalI !=
3) {
mMoveView =
new moveView(MainActivity.this, v, event, false, 0);
} else {
mMoveView =
new moveView(MainActivity.this, v, event, true, mAddImageGridView
.getmSpace());
}
} else {
if (finalI !=
4) {
mMoveView =
new moveView(MainActivity.this, v, event, false, 0);
} else {
mMoveView =
new moveView(MainActivity.this, v, event, true, mAddImageGridView
.getmSpace());
}
}mWindowManager.addView(mMoveView, mParams);
//添加移动图片,注意mParams.format =
PixelFormat.TRANSLUCENT;
fisselect =
(Bitmap) v.getTag();
//刚开始按选择的图片,
用于放手时显示,
mMoveView.setOnDragListener(new moveView.OnDragListener() {//moveView的滑动监听
@
Override
public void onDisappear() {//手抬起时
if (mMoveView !=
null &
&
mWindowManager !=
null) {
mWindowManager.removeView(mMoveView);
//移除
v.setOnTouchListener(new View.OnTouchListener() {//消失的时候覆盖触摸事件,
不然第二次就不需要按一秒了
@
Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
mMoveView =
null;
}
//Log.e("
test"
,"
curentSpace:"
+
curentSpace);
ImageView iv =
(ImageView) mViews.get(curentSpace);
iv.setImageBitmap(fisselect);
//第一次按时选择的图片
iv.setVisibility(View.VISIBLE);
iv.setTag(fisselect);
first =
false;
spacTime =
0L;
initMoveListener();
}int width =
mAddImageGridView.getmWidth();
//图片宽度
int spc =
mAddImageGridView.getmSpace();
//图片之间间隙@
Override
public void onMove(int centerX, int centerY) { //返回中心点的回调
for (int j =
0;
j <
mViews.size();
j+
+
) { //算各个图片的中心点,
依次进行比较
if (j !=
curentSpace) {
int x =
0;
int y =
0;
if (mViews.size() <
5) {
if (j <
3) { //第一排的中心点
x =
(j +
2) * width +
(j +
1) * spc - width / 2;
y =
width / 2;
} else if (j !=
curentSpace &
&
j >
=
3) { //第二排的中心点。图片宽高相等
x =
(j - 3) * (width +
spc) +
width / 2;
y =
width +
spc +
width / 2;
}
} else {
if (j <
4) { //第一排的中心点
x =
(j +
1) * width +
j * spc - width / 2;
y =
width / 2;
} else if (j !=
curentSpace &
&
j >
=
4) { //第二排的中心点。图片宽高相等
x =
(j - 4) * (width +
spc) +
width / 2;
y =
width +
spc +
width / 2;
}
}if (shouldReplace(centerX, centerY, x, y, 70)) {//如果要与J位置的view进行交换
//Log.e("
test"
,"
currenspac:"
+
curentSpace +
"
j:"
+
j);
Bitmap bitmap =
(Bitmap) mViews.get(j).getTag();
//获得j位置的bitmap
mViews.get(j).setVisibility(View.INVISIBLE);
//把j位置对应的view隐藏
ImageView iv =
(ImageView) mViews.get(curentSpace);
//原来隐藏的bitmap
iv.setImageBitmap(bitmap);
//原来隐藏的view换成j的bitmap
iv.setVisibility(View.VISIBLE);
//把原来隐藏的view显示
iv.setTag(bitmap);
//如果交换,
把交换的那个隐藏,
原来隐藏的显示,
并把图片设置为交换的、还要保持tag
curentSpace =
j;
//当前空位换为交换既隐藏的那个位置,
当松手时要显示,
最原始的的bitmap前面已经保存//Log.e("
test"
,"
currenspac:"
+
curentSpace +
"
j:"
+
j);
break;
//交换停止本次循环
}
}
}
}
});
}if (mMoveView !=
null) {
mMoveView.onTouchEvent(event);
//把触摸事件传递给move控件
}
return false;
//还要处理点击事件
}
});
}
}
等下评论区贴出完整代码。
推荐阅读
- Android快速开发偷懒必备 支持DataBinding啦~爽炸,一行实现花式列表
- React Native Android 应用层实战沦陷记
- Android RecycleView + CardView 控件简析
- android之网络编程
- Android中AlarmManager使用示例(持续更新)
- Android 7.0 UICC 分析
- Android:OpenFire 相关API (持续更新)
- Android-Sqlite数据库的操作
- Android和iOS的11大最佳家庭定位器应用软件推荐合集