[Android] Drag and dorp ListView
因為客製化的需求,第一次嘗試在Settings 中添加新的功能
其中一項為 ListView 的物件可以拖拉排序
參考的為 Music 中的 TrackBrowserActivity.java 與 TouchInterceptor.java
首先先自訂一個Layout for activity
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.android.alucard.DragListView
android:id="@+id/drag_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000"/>
</LinearLayout>
因為要實現能drag and drop 的效果,使用原生的ImageView or ListView 無法實現
所以寫了一個新的View (繼承ListView) 並在Layout中使用他.
以下是 DragListView 的實現
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;
public class DragListView extends ListView {
private ImageView dragImageView;
private int dragSrcPosition;
private int dragPosition;
private int dragPoint;
private int dragOffset;
private Context mContext;
private WindowManager windowManager;
private WindowManager.LayoutParams windowParams;
private int scaledTouchSlop;
private int upScrollBounce;
private int downScrollBounce;
private DropListener mDropListener;
public DragListView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
public interface DropListener {
void drop(int dragPosition ,int dropPosition);
}
public void setDropListener(DropListener l) {
mDropListener = l;
}
//
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(ev.getAction() == MotionEvent.ACTION_DOWN){
int x = (int) ev.getX();
int y = (int) ev.getY();
dragSrcPosition = dragPosition = pointToPosition(x, y);
if(dragPosition == AdapterView.INVALID_POSITION){
return super.onInterceptTouchEvent(ev);
}
ViewGroup itemView = (ViewGroup) getChildAt(dragPosition - getFirstVisiblePosition());
dragPoint = y - itemView.getTop();
dragOffset = (int) (ev.getRawY() - y);
View dragger = itemView.findViewById(R.id.drag_list_item_image);
if(dragger !=null && x > dragger.getLeft() - 20){
upScrollBounce = Math.min(y-scaledTouchSlop, getHeight()/3);
downScrollBounce = Math.max(y+scaledTouchSlop, getHeight()*2/3);
itemView.setDrawingCacheEnabled(true);
Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());
bm.setHasAlpha(true);
startDrag(bm, y);
}
return false;
}
return super.onInterceptTouchEvent(ev);
}
/**
*
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
if(dragImageView != null && dragPosition != INVALID_POSITION){
int action = ev.getAction();
switch(action){
case MotionEvent.ACTION_UP:
int upY = (int)ev.getY();
stopDrag();
onDrop(upY);
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int)ev.getY();
onDrag(moveY);
break;
default:break;
}
return true;
}
//
return super.onTouchEvent(ev);
}
/**
*
* @param bm
* @param y
* 在拖曳的時候,被拖曳的image會放大120%
*/
public void startDrag(Bitmap bm ,int y){
stopDrag();
float disreHeight = bm.getHeight() * (float) 1.2;
float disreWidth = bm.getWidth() * (float) 1.2;
windowParams = new WindowManager.LayoutParams();
windowParams.gravity = Gravity.TOP;
windowParams.x = 0;
windowParams.y = y - dragPoint + dragOffset;
windowParams.width = (int) disreWidth;
windowParams.height = (int )disreHeight;
windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
windowParams.format = PixelFormat.TRANSLUCENT;
windowParams.alpha=0.6f;
windowParams.windowAnimations = 0;
ImageView imageView = new ImageView(getContext());
imageView.setBackgroundColor(Color.WHITE);
imageView.setImageBitmap(bm);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
windowManager = (WindowManager)getContext().getSystemService("window");
windowManager.addView(imageView, windowParams);
dragImageView = imageView;
}
/**
*
*/
public void stopDrag(){
if(dragImageView != null){
windowManager.removeView(dragImageView);
dragImageView = null;
}
}
/**
* 因為需求只要求可以上下移動,故只取縱向的數據
*
*/
public void onDrag(int y){
if(dragImageView != null){
windowParams.alpha = 0.8f;
windowParams.y = y - dragPoint + dragOffset;
windowManager.updateViewLayout(dragImageView, windowParams);
}
//
int tempPosition = pointToPosition(0, y);
if(tempPosition != INVALID_POSITION){
dragPosition = tempPosition;
}
int scrollHeight = 0;
if( y < upScrollBounce){
scrollHeight = 8;
}else if(y > downScrollBounce){
scrollHeight = -8;
}
if(scrollHeight!=0){
setSelectionFromTop(dragPosition, getChildAt(dragPosition - getFirstVisiblePosition()).getTop() + scrollHeight);
}
}
/**
* 因為需求只要求可以上下移動,故只取縱向的數據
*
*/
public void onDrop(int y){
int tempPosition = pointToPosition(0, y);
if(tempPosition != INVALID_POSITION){
dragPosition = tempPosition;
}
if(y < getChildAt(1).getTop()){
dragPosition = 0;
}else if(y > getChildAt(getChildCount()-1).getBottom()){
dragPosition = getAdapter().getCount()-1;
}
if(dragPosition >= 0 && dragPosition < getAdapter().getCount()){
mDropListener.drop(dragSrcPosition, dragPosition);
}
}
}
在Activity中 調用 Adapter, 並且倒入到剛剛的View中.
Activity:
import android.content.Intent;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import android.widget.WrapperListAdapter;
import android.content.Context;
import android.provider.Settings;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.os.Bundle;
import android.util.Log;
public class LockFuncSettings extends Fragment {
private final String TAG = "LockFuncSettings";
private Context mContext;
private View mlistview;
private List<String> mFunctionList;
private DragListAdapter adapter;
private DragListView dragListView;
private final String KEY_LOCK_FUNC_SETTING = "lockscrren_func_setting_pref";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getActivity();
mFunctionList = new ArrayList<String>();
loadFuncList();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mlistview = inflater.inflate(R.layout.drag_list_activity, null);
dragListView = (DragListView) mlistview.findViewById(R.id.drag_list);
return mlistview;
}
private void loadFuncList() {
String list = Settings.System.getString(mContext.getContentResolver(), Settings.System.LOCKSCREEN_FUNC_LIST);
for (String s : list.split(" ")) {
mFunctionList.add(s);
}
mFunctionList.add(5, "12");
}
@Override
public void onResume() {
adapter = new DragListAdapter(mContext, mFunctionList);
dragListView.setDropListener(mDropListener);
dragListView.setAdapter(adapter);
super.onResume();
}
@Override
public void onPause() {
super.onPause();
}
private DragListView.DropListener mDropListener =
new DragListView.DropListener() {
public void drop(int dragPosition ,int dropPosition) {
if (adapter != null) {
String dragItem = (String) adapter.getItem(dragPosition);
Log.d("LockFuncSettings", "dragItem = " + dragItem);
adapter.remove(dragItem);
adapter.insert(dragItem, dropPosition);
//---------------------------------------------------------
adapter.remove("12");
String result = "";
for(int i = 0; i < 12; i++) {
result = result + (String) adapter.getItem(i) + " ";
}
Log.d(TAG, "result = " + result);
Settings.System.putString(mContext.getContentResolver(),
Settings.System.LOCKSCREEN_FUNC_LIST, result.trim());
adapter.insert("12", 5);
adapter.notifyDataSetChanged();
mContext.sendBroadcast(new Intent("android.intent.action.FUNC_SETTINGS_CHANGED"));
}
}
};
}
Adapter:
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.ImageView;
import android.provider.Settings;
import android.util.Log;
public class DragListAdapter extends ArrayAdapter<String> {
public List<String> groupKey = new ArrayList<String>();
public DragListAdapter(Context context, List<String> objects) {
super(context, 0, objects);
groupKey.add(getContext().getString(R.string.lock_settings_wont_show));
}
@Override
public boolean isEnabled(int position) {
if(groupKey.contains(getItem(position))){
//
return false;
}
return super.isEnabled(position);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if(position == 5) {
view = LayoutInflater.from(getContext()).inflate(R.layout.drag_list_item_tag, null);
}else{
view = LayoutInflater.from(getContext()).inflate(R.layout.drag_list_item, null);
}
TextView textView = (TextView)view.findViewById(R.id.drag_list_item_text);
ImageView imageView = (ImageView) view.findViewById(R.id.drag_list_item_icon);
int id = Integer.parseInt(getItem(position));
boolean isBlue = (position < 5)? true:false;
switch (id) {
case Settings.System.FUNC_CALCULATOR:
imageView.setImageResource(isBlue? R.drawable.list_calculator_blue:R.drawable.list_calculator_grey);
textView.setText(R.string.func_calculator);
break;
case Settings.System.FUNC_PLAYLIST:
imageView.setImageResource(isBlue? R.drawable.list_playlist_blue:R.drawable.list_playlist_grey);
textView.setText(R.string.func_playlist);
break;
case Settings.System.FUNC_SELFIE:
imageView.setImageResource(isBlue? R.drawable.list_selfie_blue:R.drawable.list_selfie_grey);
textView.setText(R.string.func_selfie);
break;
case Settings.System.FUNC_CONTACT:
imageView.setImageResource(isBlue? R.drawable.list_contact_blue:R.drawable.list_contact_grey);
textView.setText(R.string.func_contact);
break;
case Settings.System.FUNC_QR_CODE:
imageView.setImageResource(isBlue? R.drawable.list_qr_blue:R.drawable.list_qr_grey);
textView.setText(R.string.func_qr_code);
break;
case Settings.System.FUNC_MESSAGE:
imageView.setImageResource(isBlue? R.drawable.list_mes_blue:R.drawable.list_mes_grey);
textView.setText(R.string.func_message);
break;
case Settings.System.FUNC_EMAIL:
imageView.setImageResource(isBlue? R.drawable.list_email_blue:R.drawable.list_email_grey);
textView.setText(R.string.func_email);
break;
case Settings.System.FUNC_AGENDA:
imageView.setImageResource(isBlue? R.drawable.list_event_blue:R.drawable.list_event_grey);
textView.setText(R.string.func_agenda);
break;
case Settings.System.FUNC_RECORD:
imageView.setImageResource(isBlue? R.drawable.list_record_blue:R.drawable.list_record_grey);
textView.setText(R.string.func_record);
break;
case Settings.System.FUNC_HOME:
imageView.setImageResource(isBlue? R.drawable.list_naigation_blue:R.drawable.list_naigation_grey);
textView.setText(R.string.func_home);
break;
case Settings.System.FUNC_ALARM:
imageView.setImageResource(isBlue? R.drawable.list_alarm_blue:R.drawable.list_alarm_grey);
textView.setText(R.string.func_alarm);
break;
case Settings.System.FUNC_NOTE:
imageView.setImageResource(isBlue? R.drawable.list_note_blue:R.drawable.list_note_grey);
textView.setText(R.string.func_note);
break;
default:
imageView.setImageResource(R.drawable.list_arrow);
textView.setText(R.string.lock_settings_wont_show);
}
if (!isBlue) {
view.setBackgroundColor(Color.parseColor("#e5e5e5"));
textView.setTextColor(Color.parseColor("#a0a0a0"));
}
return view;
}
}
在DragListView中的物件,有兩個xml
第一個是可以拖拉物件的layout 定義
請注意,一定要用 RelativeLayout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView android:id="@+id/drag_list_item_icon"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_marginStart="@dimen/search_result_item_image_margin_start"
android:layout_marginEnd="@dimen/search_result_item_image_margin_end"
android:layout_height="@dimen/drag_item_normal_height"/>
<TextView
android:id="@+id/drag_list_item_text"
android:layout_width="wrap_content"
android:layout_height="@dimen/drag_item_normal_height"
android:layout_marginLeft="75dp"
android:layout_alignParentStart="true"
android:textSize="16sp"
android:gravity="center_vertical"/>
<ImageView android:id="@+id/drag_list_item_image"
android:src="@drawable/list_menu"
android:layout_marginRight="15dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="@dimen/drag_item_normal_height"/>
</RelativeLayout>
第二個為不可拖拉的物件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#a0a0a0"
android:padding="5dip"
android:paddingLeft="10dip">
<ImageView android:id="@+id/drag_list_item_icon"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_marginStart="@dimen/search_result_item_image_margin_start"
android:layout_marginEnd="@dimen/search_result_item_image_margin_end"
android:layout_height="@dimen/drag_item_normal_height"/>
<TextView
android:id="@+id/drag_list_item_text"
android:layout_width="wrap_content"
android:layout_height="20dip"
android:textColor="#ffffff"
android:textSize="14sp"
android:layout_marginStart="@dimen/settings_side_margin"
android:layout_marginEnd="@dimen/settings_side_margin"
android:gravity="center_vertical"/>
</LinearLayout>