Android自定义view 滑动开关 支持左右滑动 适用于listview
要做这样一种开关。
当开关在左边时,都是灰色的,向右滑动的时候,滑到一半的时候,改变颜色,变成绿色;
当开关在右边是,都市绿色的,向左滑动的时候,滑动一半的时候,改变颜色,变成灰色。
这里就要涉及要可滑动最大距离,以及你现在滑动的距离。通过这个来比较,改变颜色。
import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; /** * 自定义开关按钮, * * */ public class PushSlideSwitchView extends View{ /** Switch底部灰色样式图片 */ private Bitmap mSwitchBgUnseleted; /** Switch底部绿色样式图 */ private Bitmap mSwitchBgSeleted; /** Switch灰色的球 */ private Bitmap mSwitchBallUnseleted; /** Switch绿色的球 */ private Bitmap mSwitchBallSeleted; private float mCurrentX = 0; /** Switch 开关状态,默认是 开:true */ private boolean mSwitchOn = true; /** Switch 最大移动距离 */ private int mMoveLength; /** 第一次按下的有效区域 */ private float mLastX = 0; /** 绘制的目标区域大小 */ private Rect mDest = null; /** 截取源图片的大小 */ /** Switch 移动的偏移量 */ private int mMoveDeltX = 0; /** 画笔工具 */ private Paint mPaint = null; /** Switch 状态监听接口 */ private OnSwitchChangedListener switchListener = null; private boolean mFlag = false; /** enabled 属性 为 true */ private boolean mEnabled = true; /** 最大透明度,就是不透明 */ private final int MAX_ALPHA = 255; /** 当前透明度,这里主要用于如果控件的enable属性为false时候设置半透明 ,即不可以点击 */ private int mAlpha = MAX_ALPHA; /** Switch 判断是否在拖动 */ private boolean mIsScrolled =false; public PushSlideSwitchView(Context context) { this(context, null); } public PushSlideSwitchView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PushSlideSwitchView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } /** * 初始化相关资源 */ public void init() { mSwitchBgSeleted = BitmapFactory.decodeResource(getResources(),R.drawable.push_button_selected_bg); mSwitchBgUnseleted = BitmapFactory.decodeResource(getResources(),R.drawable.push_button_unselected_bg); mSwitchBallSeleted = BitmapFactory.decodeResource(getResources(),R.drawable.push_button_ball_selected); mSwitchBallUnseleted = BitmapFactory.decodeResource(getResources(),R.drawable.push_button_ball_unselected); mMoveLength = mSwitchBgSeleted.getWidth() - mSwitchBallSeleted.getWidth(); //绘制区域大小 mDest = new Rect(0, 0, mSwitchBgSeleted.getWidth(), mSwitchBgSeleted.getHeight()); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setAlpha(255); mPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(mSwitchBgSeleted.getWidth(), mSwitchBgSeleted.getHeight()); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); /*System.out.println("---onDraw()---mMoveDeltX= " + mMoveDeltX + " mSwitchBgUnseleted.getWidth()= " + mSwitchBgUnseleted.getWidth() + " mSwitchBallSeleted.getWidth()= " + mSwitchBallSeleted.getWidth() + " mMoveLength = " + mMoveLength);*/ canvas.saveLayerAlpha(new RectF(mDest), mAlpha, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); //如果是关闭的 if(!mSwitchOn){ if(mMoveDeltX > 0){//向右滑动了 if(mMoveDeltX < mMoveLength/2){//滑动距离小于一半 canvas.drawBitmap(mSwitchBgUnseleted, 0, 0, null); //灰色背景 canvas.drawBitmap(mSwitchBallUnseleted, mMoveDeltX, 0, null); //灰色按钮 }else{//滑动距离大于一半 canvas.drawBitmap(mSwitchBgSeleted, 0, 0, null); //绿色背景 canvas.drawBitmap(mSwitchBallSeleted, mMoveDeltX, 0, null); //绿色按钮 } }else{ canvas.drawBitmap(mSwitchBgUnseleted, 0, 0, null); //灰色背景 canvas.drawBitmap(mSwitchBallUnseleted, 0, 0, null); //灰色按钮 } }else{ if(mMoveDeltX < 0){//向右滑动了 if(Math.abs(mMoveDeltX) < mMoveLength/2){//滑动距离小于一半 canvas.drawBitmap(mSwitchBgSeleted, 0, 0, null); //绿色背景 canvas.drawBitmap(mSwitchBallSeleted, mSwitchBgSeleted.getWidth() - mSwitchBallSeleted.getWidth() + mMoveDeltX, 0, null); //绿色按钮 }else{//滑动距离大于一半 canvas.drawBitmap(mSwitchBgUnseleted, 0, 0, null); //灰色背景 canvas.drawBitmap(mSwitchBallUnseleted, mSwitchBgSeleted.getWidth() - mSwitchBallSeleted.getWidth() + mMoveDeltX, 0, null); //灰色按钮 } }else{ canvas.drawBitmap(mSwitchBgSeleted, 0, 0, null); //绿色背景 canvas.drawBitmap(mSwitchBallSeleted, mMoveLength, 0, null); //绿色按钮 } } canvas.restore(); } @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub //如果Enabled属性设定为true,触摸效果才有效 if(!mEnabled){ return true; } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mLastX = event.getX(); break; case MotionEvent.ACTION_MOVE: mCurrentX = event.getX(); mMoveDeltX = (int) (mCurrentX - mLastX); //System.out.println("===============" + mMoveDeltX); if(mMoveDeltX > 3){ //设置了3这个误差距离,可以更好的实现点击效果 mIsScrolled = true; } // 如果开关开着向右滑动,或者开关关着向左滑动(这时候是不需要处理的) if ((mSwitchOn && mMoveDeltX > 0) || (!mSwitchOn && mMoveDeltX < 0)) { mFlag = true; mMoveDeltX = 0; } if (Math.abs(mMoveDeltX) > mMoveLength) { mMoveDeltX = mMoveDeltX > 0 ? mMoveLength : -mMoveLength; } invalidate(); break; case MotionEvent.ACTION_UP: //如果没有滑动过,就看作一次点击事件 if(!mIsScrolled){ mMoveDeltX = mSwitchOn ? mMoveLength : -mMoveLength; mSwitchOn = !mSwitchOn; if (switchListener != null) { switchListener.onSwitchChange(this, mSwitchOn); } invalidate(); mMoveDeltX = 0; break; } mIsScrolled = false; if (Math.abs(mMoveDeltX) > 0 && Math.abs(mMoveDeltX) < mMoveLength / 2) { mMoveDeltX = 0; invalidate(); } else if (Math.abs(mMoveDeltX) > mMoveLength / 2 && Math.abs(mMoveDeltX) <= mMoveLength) { mMoveDeltX = mMoveDeltX > 0 ? mMoveLength : -mMoveLength; mSwitchOn = !mSwitchOn; if (switchListener != null) { switchListener.onSwitchChange(this, mSwitchOn); } invalidate(); mMoveDeltX = 0; } else if (mMoveDeltX == 0 && mFlag) { // 这时候得到的是不需要进行处理的,因为已经move过了 mMoveDeltX = 0; mFlag = false; } default: break; } invalidate(); return true; } /** * 设置 switch 状态监听 * */ public void setOnChangeListener(OnSwitchChangedListener listener) { switchListener = listener; } /** * switch 开关监听接口 * */ public interface OnSwitchChangedListener{ public void onSwitchChange(PushSlideSwitchView switchView, boolean isChecked); } @Override public void setEnabled(boolean enabled) { // TODO Auto-generated method stub mEnabled = enabled; mAlpha = enabled ? MAX_ALPHA : MAX_ALPHA/2; Log.d("enabled",enabled ? "true": "false"); super.setEnabled(enabled); invalidate(); } /** 自动判断切换至相反的属性 : true -->false ;false -->true */ public void toggle() { setChecked(!mSwitchOn); } /** 设置选中的状态(选中:true 非选中: false) */ public void setChecked(boolean checked) { mSwitchOn = checked; invalidate(); } }
就是这样一个类,就可以了。
在xml文件中可以这样引用:
<com.app.view.PushSlideSwitchView android:id="@+id/push_set_warm_switchview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginRight="15dip" android:enabled="true" />
在这里附上需要引用的四张图片
在java代码中我们通过设置switchView.setOnChangeListener()来监听是否打开和关闭。
如果是单个的使用,使用上面的完全没有问题。
但是这里还涉及到一个问题,如果是listview,假如每个item里面都有一个这样的开关,在应用权限设置里面可能会看到这样的操作场景。
以前用其他的自定义滑动开关会发现,如果上下滑动几次,就会乱套,左边的会跳到右边,有的右边会跳到左边。这个也解决了这个问题。
java代码如下
package com.example.compoundbuttonview; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import com.example.compoundbuttonview.view.MySlideSwitchView; import com.example.compoundbuttonview.view.MySlideSwitchView.OnSwitchChangedListener; /** */ public class MainActivity2 extends Activity { private ListView listview; List<Map<String, Object>> tempListData = new ArrayList<Map<String, Object>>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main2); initView(); for (int i = 0; i < 50; i++) { HashMap datamap = new HashMap<String, Object>(); datamap.put("index", i+1); datamap.put("checked", i%2==0?true:false); tempListData.add(datamap); } MyAdapter adapter = new MyAdapter(MainActivity2.this); listview.setAdapter(adapter); } private void initView() { listview = (ListView) findViewById(R.id.listView1); } class MyAdapter extends BaseAdapter { private LayoutInflater inflater = null; private Context context; public MyAdapter(Context context) { this.context = context; inflater = LayoutInflater.from(context); } @Override public int getCount() { return tempListData.size(); } @Override public Object getItem(int position) { return tempListData.get(position); } @Override public long getItemId(int position) { return position; } public void modifyStates(int position) { } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = inflater.inflate(R.layout.main2_listview_item, null); holder = new ViewHolder(); holder.index = (TextView) convertView.findViewById(R.id.item_index); holder.SlideSwitchView = (MySlideSwitchView) convertView.findViewById(R.id.item_SwitchView); // 使用tag来存储数据 convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.index.setText(tempListData.get(position).get("index") + ""); holder.SlideSwitchView.setChecked((Boolean) tempListData.get(position).get("checked")); holder.SlideSwitchView.setOnChangeListener(new OnSwitchChangedListener() { @Override public void onSwitchChange(MySlideSwitchView switchView, boolean isChecked) { tempListData.get(position).put("checked", isChecked); } }); return convertView; } } class ViewHolder { public MySlideSwitchView SlideSwitchView; public TextView index; } }
xml布局文件如下:
main2.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android1="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="1dip" > <ListView android1:id="@+id/listView1" android1:layout_width="match_parent" android1:layout_height="wrap_content" > </ListView> </LinearLayout>
item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android1="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="1dip" > <ListView android1:id="@+id/listView1" android1:layout_width="match_parent" android1:layout_height="wrap_content" > </ListView> </LinearLayout>
好了,由于代码全部贴出来了。就没有做成资源去下载,代码有注释,看一下就懂,其实很好用。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。