自定義可點選和滑動的按鈕
一. 在最近的專案中需要實現一個這樣的自定義View,有點類似一個開關選擇按鈕,可以點選、可以滑動。在按鈕下方是一個ViewPager,根據按鈕的切換,同時切換ViewPager的介面。同時滑動ViewPager,也會切換到按鈕不同的登入方式。把實現步驟記錄一下,方便以後擴充套件和複用,同時加深一下對自定義View的理解。
總結:主要解決的問題是 1. 自定義View的步驟 , 2. 點選和滑動的衝突 2. 自定義控制元件和ViewPager的聯動切換。
問題: 想實現點選的時候滑塊也可以向滑動按鈕一樣平滑的滑動到另一邊,並且讓下面的ViewPager也勻速滑動,有什麼好的方法處理
二. 看下面的幾個gif圖的演示效果。
- 滑動上面的控制元件,讓ViewPager中的Fragment進行切換。
滑動控制元件下方的ViewPager讓按鈕的滑塊進行同向移動(gif檔案過大,無法上傳)
點選控制元件,切換不同的按鈕背景
三. 實現上面三個效果的步驟:
1. 新建一個SwitchButton類繼承自View, 實現View的構造方法,測量,繪製的方法,並在xml中引用該控制元件。
構造方法:
public SwitchButton(Context context, @Nullable AttributeSet attrs) {
super (context, attrs);
initView();
}
initView方法初始化需要繪製的Bitmap和畫筆、以及繪製的一些位置引數
login_select_liner_bg是整個控制元件的大的背景圖片
要滑動的選擇器login_selected_text_bg
通過BitmapFactory.decodeResource把drawable資料夾下的png圖片轉換成Bitmap影象。
backgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.login _select_liner_bg);
slidingBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.login_selected_text_bg);
初始化繪製的時候是:slidingBitmap 在 backgroundBitmap的靠左居中, 讓slidingBitmap 距離top為slidingMarginTop 。
slidingMarginTop = (backgroundBitmap.getHeight() - slidingBitmap.getHeight()) /2;
上面Bitmap的繪製需要畫筆,初始化一個畫筆
mBitMapPaint = new Paint();
mBitMapPaint.setAntiAlias(true);
控制元件上的文字也是繪製上去的,同樣需要畫筆,黃色文字和白色文字的畫筆設定。
mDefaultTextPaint = new Paint();
mDefaultTextPaint.setAntiAlias(true);
mDefaultTextPaint.setTextSize(32);
mDefaultTextPaint.setStyle(Paint.Style.FILL);
mDefaultTextPaint.setTextAlign(Paint.Align.LEFT);
mDefaultTextPaint.setColor(Color.WHITE);
mSelectTextPaint = new Paint();
mSelectTextPaint.setAntiAlias(true);
mSelectTextPaint.setTextSize(32);
mSelectTextPaint.setStyle(Paint.Style.FILL);
mSelectTextPaint.setTextAlign(Paint.Align.LEFT);
mSelectTextPaint.setColor(getResources().getColor(R.color.orange));
- 初始化引數之後,開始進行測量:這是一個view, 設定自定義View的寬高為backgroundBitmap的寬高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
}
- 開始繪製:
slidingMarginLeft 表示滑動選擇塊離這個自定義view的左邊偏移距離
passwordLoginStr =“密碼登入”
smsLoginStr = “簡訊登入”
canvas.drawBitmap(backgroundBitmap,0 ,0, mBitMapPaint);
canvas.drawBitmap(slidingBitmap,slidingMarginLeft,slidingMarginTop, mBitMapPaint);
if (isSelectLeft) {
canvas.drawText(passwordLoginStr, textLeftMargin, textVerticalLocation, mSelectTextPaint);
canvas.drawText(smsLoginStr, slidMarginLeftMax + textLeftMargin, textVerticalLocation, mDefaultTextPaint);
} else {
canvas.drawText(passwordLoginStr, textLeftMargin, textVerticalLocation, mDefaultTextPaint);
canvas.drawText(smsLoginStr, slidMarginLeftMax + textLeftMargin, textVerticalLocation, mSelectTextPaint);
}
- 新增點選事件: 繪製出了view只後,只是如第一張圖片一樣是一個靜態的效果,需要新增點選事件。
讓View繼承自View.OnClickListener 實現onClick方法:
預設當滑到左邊的時候slidingBitmap 距離左邊為5個畫素(把畫素裝換成dp會有更好的適配效果),滑到右邊的時候距離左邊最大的距離slidMarginLeftMax = backgroundBitmap.getWidth() - slidingBitmap.getWidth() - 5(把畫素裝換成dp會有更好的適配)
isSelectLeft 表示是否點選選中左邊的密碼登入,預設為true,每點選一次進行切換,上面的文字繪製需要根據這個值進行判斷。
//實現控制元件的點選事件
@Override
public void onClick(View v) {
isSelectLeft = !isSelectLeft;
if (isSelectLeft) {
slidingMarginLeft = 5;
} else {
slidingMarginLeft = slidMarginLeftMax;
}
//isChangeComplete = true;
invalidate();
}
在 initView 中 加上setOnClickListener(this);
這樣就能相應點選事件了。
- 除了點選效果外,還需要新增滑動處理:實現onTouchEvent方法
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
//計算按下的座標
isStartX = startX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
float endX = event.getX();
float distanceX = endX - startX;
slidingMarginLeft += distanceX;
//防止將滑塊滑出介面,遮蔽非法值
if(slidingMarginLeft > slidMarginLeftMax){
slidingMarginLeft = slidMarginLeftMax;
}else if(slidingMarginLeft < 5){
slidingMarginLeft = 5;
}
invalidate();
//資料還原
startX = event.getX();
break;
case MotionEvent.ACTION_UP:
if (slidingMarginLeft > slidMarginLeftMax / 2) {
slidingMarginLeft = slidMarginLeftMax;
isSelectLeft = false;
} else {
slidingMarginLeft = 5;
isSelectLeft = true;
}
invalidate();
break;
}
return true;
}
- 在添加了點選和觸控滑動效果之後會出現衝突效果,解決衝突的辦法是,宣告一個boolean值的變數isEnableClick,如果boolean 為true表示點選事件生效,當活動觸控是,將其設定為false, 表示點選事件不生效。
修改之後的點選事件onClick方法:
@Override
public void onClick(View v) {
if(isEnableClick) {
isSelectLeft = !isSelectLeft;
if (isSelectLeft) {
slidingMarginLeft = 5;
} else {
slidingMarginLeft = slidMarginLeftMax;
}
//isChangeComplete = true;
invalidate();
}
}
觸控事件 onTouchEvent也做相應的修改。
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
//計算按下的座標
isStartX = startX = event.getX();
isEnableClick = true;
break;
case MotionEvent.ACTION_MOVE:
float endX = event.getX();
float distanceX = endX - startX;
slidingMarginLeft += distanceX;
//遮蔽非法值
if(slidingMarginLeft > slidMarginLeftMax){
slidingMarginLeft = slidMarginLeftMax;
}else if(slidingMarginLeft < 5){
slidingMarginLeft = 5;
}
//isChangeComplete = false;
invalidate();
//資料還原
startX = event.getX();
if(Math.abs(endX - isStartX) > 5){
//已經滑動了 就不再執行點選事件
isEnableClick = false;
}
break;
case MotionEvent.ACTION_UP:
if(!isEnableClick) {
if (slidingMarginLeft > slidMarginLeftMax / 2) {
slidingMarginLeft = slidMarginLeftMax;
isSelectLeft = false;
} else {
slidingMarginLeft = 5;
isSelectLeft = true;
}
invalidate();
}
break;
}
return true;
}
- 實現與ViewPager的聯動方式一:點選按鈕切換ViewPager
在View中第一個點選監聽的interface
public interface ChangeSelectListener{
void changeSelect(boolean isSelectLeft);
}
public void setChangeSelectListener(ChangeSelectListener changeSelectListener){
mChangeSelectListener = changeSelectListener;
}
在onClick方法中加上回調方法
if(mChangeSelectListener != null && isEnableClick) {
mChangeSelectListener.changeSelect(isSelectLeft);
}
- XXActivity的xml中定義如下
<LinearLayout
android:id="@+id/login_model_switch_liner"
android:layout_width="wrap_content"
android:orientation="horizontal"
android:layout_height="0dp"
android:layout_weight="1.5"
android:gravity="center_vertical">
<kap.com.smarthome.android.ui.view.SwitchButton
android:id="@+id/switchButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/margin_20"
/>
</LinearLayout>
<android.support.v4.view.ViewPager
android:id="@+id/login_view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="15dp"
android:layout_weight="9.5">
</android.support.v4.view.ViewPager>
在 XXActivity中ViewPager的處理程式碼:
//設定ViewPager的介面卡, 設定展示的fragment
mMethodViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
});
//給ViewPager新增切換的監聽器
mMethodViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//滑動ViewPager的時候讓mSwitchButton的滑塊跟著按照比例跟著滑動, //setSlidingMarginLeft的方法下面給出
mSwitchButton.setSlidingMarginLeft(positionOffset);
}
@Override
public void onPageSelected(int position) {
//當滑動結束之後,設定是滑動到左邊還是右半邊
//SwitchButton中的setSelectLeft方法後面給出
if(position == 0){
mSwitchButton.setSelectLeft(true);
}else{
mSwitchButton.setSelectLeft(false);
}
}
@Override
public void onPageScrollStateChanged(int state) {}
});
mListener = new SwitchButton.ChangeSelectListener() {
@Override
public void changeSelect(boolean isSelectLeft) {
if(isSelectLeft){
mMethodViewPager.setCurrentItem(0);
}else{
mMethodViewPager.setCurrentItem(1);
}
}
};
mSwitchButton.setChangeSelectListener(mListener);
- SwitchButton類中的setSlidingMarginLeft方法 傳入的scalex是viewpager的滑動比例,根據這個值可以計算出當前slidingMarginLeft 的值
public void setSlidingMarginLeft(float scaleX){
if(scaleX != 0) {
slidingMarginLeft = (int) (slidMarginLeftMax * scaleX);
invalidate();
}
}
- SwitchButton類中的setSelectLeft方法, 表示ViewPager滑動結束,slidingMarginLeft 重新設定最終值
public void setSelectLeft(boolean selectLeft) {
isSelectLeft = selectLeft;
if (isSelectLeft) {
slidingMarginLeft = 5;
} else {
slidingMarginLeft = slidMarginLeftMax;
}
invalidate();
}
下面給出SwitchButton類的完整程式碼, ViewPager的程式碼在上面第8部分已經基本給出。
/**
* Created by Administrator on 2017/9/22 0022.
*
* 建立自定義View用於登入介面中密碼登入和簡訊登入的切換
*
* 1.構造方法,例項化類
* 2.開始測量 measure(int , int) -> onMeasure();
* 如果當前view是一個ViewGroup,還有義務測量自己的孩子,孩子有建議權
* 3. 指定位置-layout()--> onlayout();
* 指定控制元件的位置,一般view不用寫這個方法,ViewGroup的時候才需要,一般View不需要重寫該方法
* 4. 繪製檢視 -- draw() --> onDraw(Canvas)
* 根據上面兩個方法的引數,進行繪製
*/
public class SwitchButton extends View implements View.OnClickListener{
private Bitmap backgroundBitmap;
private Bitmap slidingBitmap;
private int slidMarginLeftMax;
private Paint mBitMapPaint;
private Paint mDefaultTextPaint;
private Paint mSelectTextPaint;
private int slidingMarginTop;
private int slidingMarginLeft = 5;
private int textVerticalLocation;
private int textLeftMargin = 20;
//是否選擇左邊
private boolean isSelectLeft = true;
//預設點選事件生效 ,滑動事件不生效
private boolean isEnableClick = true;
//橫向滑動的X軸起點座標
private float startX;
private float isStartX;
private String passwordLoginStr;
private String smsLoginStr;
private ChangeSelectListener mChangeSelectListener;
//判斷與之監聽的 ViewPager 是否滑動結束 , 只有當滑動結束的時候文字才變化。
/*private boolean isChangeComplete = false ;
private boolean isFirstDraw = true;*/
public SwitchButton(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView();
}
private void initView(){
backgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.login_select_liner_bg);
slidingBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.login_selected_text_bg);
passwordLoginStr = getResources().getString(R.string.password_login);
smsLoginStr = getResources().getString(R.string.sms_login);
slidMarginLeftMax = backgroundBitmap.getWidth() - slidingBitmap.getWidth() - 5;
slidingMarginTop = (backgroundBitmap.getHeight() - slidingBitmap.getHeight()) /2 - 2;
textVerticalLocation = backgroundBitmap.getHeight()/2+5;
mBitMapPaint = new Paint();
mBitMapPaint.setAntiAlias(true);
mDefaultTextPaint = new Paint();
mDefaultTextPaint.setAntiAlias(true);
mDefaultTextPaint.setTextSize(32);
mDefaultTextPaint.setStyle(Paint.Style.FILL);
mDefaultTextPaint.setTextAlign(Paint.Align.LEFT);
mDefaultTextPaint.setColor(Color.WHITE);
mSelectTextPaint = new Paint();
mSelectTextPaint.setAntiAlias(true);
mSelectTextPaint.setTextSize(32);
mSelectTextPaint.setStyle(Paint.Style.FILL);
mSelectTextPaint.setTextAlign(Paint.Align.LEFT);
mSelectTextPaint.setColor(getResources().getColor(R.color.orange));
setOnClickListener(this);
}
/**
* 測量這個view的大小
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
}
/**
* 進行繪製
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(backgroundBitmap,0 ,0, mBitMapPaint);
canvas.drawBitmap(slidingBitmap,slidingMarginLeft,slidingMarginTop, mBitMapPaint);
drawText(canvas);
}
/**
* 繪製文字
* @param canvas
*/
private void drawText(Canvas canvas) {
if (isSelectLeft) {
canvas.drawText(passwordLoginStr, textLeftMargin, textVerticalLocation, mSelectTextPaint);
canvas.drawText(smsLoginStr, slidMarginLeftMax + textLeftMargin, textVerticalLocation, mDefaultTextPaint);
} else {
canvas.drawText(passwordLoginStr, textLeftMargin, textVerticalLocation, mDefaultTextPaint);
canvas.drawText(smsLoginStr, slidMarginLeftMax + textLeftMargin, textVerticalLocation, mSelectTextPaint);
}
}
//實現控制元件的點選事件
@Override
public void onClick(View v) {
if(isEnableClick) {
isSelectLeft = !isSelectLeft;
if (isSelectLeft) {
slidingMarginLeft = 5;
} else {
slidingMarginLeft = slidMarginLeftMax;
}
if(mChangeSelectListener != null && isEnableClick) {
mChangeSelectListener.changeSelect(isSelectLeft);
}
invalidate();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
//計算按下的座標
isStartX = startX = event.getX();
isEnableClick = true;
break;
case MotionEvent.ACTION_MOVE:
float endX = event.getX();
float distanceX = endX - startX;
slidingMarginLeft += distanceX;
//遮蔽非法值
if(slidingMarginLeft > slidMarginLeftMax){
slidingMarginLeft = slidMarginLeftMax;
}else if(slidingMarginLeft < 5){
slidingMarginLeft = 5;
}
//isChangeComplete = false;
invalidate();
//資料還原
startX = event.getX();
if(Math.abs(endX - isStartX) > 5){
//已經滑動了 就不再執行點選事件
isEnableClick = false;
}
break;
case MotionEvent.ACTION_UP:
if(!isEnableClick) {
if (slidingMarginLeft > slidMarginLeftMax / 2) {
slidingMarginLeft = slidMarginLeftMax;
isSelectLeft = false;
} else {
slidingMarginLeft = 5;
isSelectLeft = true;
}
invalidate();
}
break;
}
return true;
}
public boolean isSelectLeft() {
return isSelectLeft;
}
public void setSelectLeft(boolean selectLeft) {
isSelectLeft = selectLeft;
if (isSelectLeft) {
slidingMarginLeft = 5;
} else {
slidingMarginLeft = slidMarginLeftMax;
}
invalidate();
}
public void setSlidingMarginLeft(float scaleX){
if(scaleX != 0) {
slidingMarginLeft = (int) (slidMarginLeftMax * scaleX);
invalidate();
}
}
public interface ChangeSelectListener{
void changeSelect(boolean isSelectLeft);
}
public void setChangeSelectListener(ChangeSelectListener changeSelectListener){
mChangeSelectListener = changeSelectListener;
}
}
相關推薦
自定義可點選和滑動的按鈕
一. 在最近的專案中需要實現一個這樣的自定義View,有點類似一個開關選擇按鈕,可以點選、可以滑動。在按鈕下方是一個ViewPager,根據按鈕的切換,同時切換ViewPager的介面。同時滑動ViewPager,也會切換到按鈕不同的登入方式。把實現步驟記錄一
Android星星評分控制元件SimpleRatingBar的使用(可點選和滑動星星)
Android星星評分控制元件SimpleRatingBar的使用 有一個專案需求,需要一個星星評分的控制元件,Android原生的RatingBar十分難用,而且還很醜,在網上找了很久,找到一個很好用的評分控制元件,在此記錄和分享一下 1.使用方法
Android 自定義帶點選效果的圓角按鈕
在開發過程中, 經常會用到一些帶點選效果的自定義圓角按鈕,特寫一篇部落格來記錄一下如何利用selector和shape組合的方式來實現。現在res/drawable資料夾下建立一個新的btn_norma
cocos2dx 3.0 lua 關於listview和item點選和滑動的觸控吞噬問題
cocos版本3.15,使用studio編輯介面 -- 建立item local item1 =Item:create(array[index])item1:setPosition(pos) self.ui.listview:addChild(item1) item是
自定義Material點選效果的View
最近在做專案的時候,遇到一個需求,需要自定義一個View;寫到佈局檔案裡面,希望也有Material的波紋點選效果,需要怎麼弄呢? ?attr/selectableItemBackground 將該View的background屬性設為標題的樣式即可,這樣在5.0以上就有了波紋效
Android RecyclerView自帶原生點選和長按事件
RecyclerView 系統自帶點選和長按事件(可複用) 寫在前面 我們用過RecyclerView的朋友都知道,它是不能直接設定其點選事件的,系統並沒有給我們提供api,不像TextView,Button等控制元件,可以直接setOnClickListener(),
自定義元件 點選空白處隱藏
程式碼實現: <template> <div> <div class="show" v-show="show" v-clickoutside="handleClose"> 顯示 </div>
高德地圖自定義Marker點選時出現的InfoWindow
1.自定義InfoWindowAdapter: package com.onetoo.www.onetoo.abapter.home; import android.content.Context;
自定義Dialog點選彈框外的區域無法關閉問題
最近在實現一個自定義Dialog時,產品要求點選彈框外的區域要能夠關閉Dialog,本來以為很簡單的,只需加一行程式碼: setCanceledOnTouchOutside(true); 就解決了嘛,結果呢,不!管!用! 好吧,既然出了問題,那就找找原因吧
一個自定義dialog提供確定和取消按鈕的回撥介面
public class MessageDialog { Context context; AlertDialog dialog; AlertDialog.Builder bu
自定義PopupWindow,點選彈出PopupWindow,背景變暗,仿點選彈出分享
注:參照大神程式碼寫的 自定義程式碼 package com.duanlian.popupwindowdemo; import android.app.Activity; import android.content.Context; import android.g
android自定義可拖動的懸浮按鈕
在頁面上隨意拖動的按鈕public class MoveScaleRotateView extends RelativeLayout { private Context mContext; //預設的觸控點ID private static fina
關於百度地圖InfoWindow響應自定義佈局點選事件
大概講解:在百度地圖上顯示一個marker,當marker被點選後,會彈出一個自定義view,當時在公司做這個功能,被坑慘了,死活彈不出來,不響應.接下來看一下效果圖,程式碼有詳細註釋,進去之後把百度申請的祕鑰換成自己的.有一部分是檢測網路程式碼.這個不用管.程式碼如下:pu
Android自定義可拖拽的懸浮按鈕---DragFloatingActionButton
懸浮按鈕FloatingActionButton是Android 5.0系統新增的新控制元件,FloatingActionButton是繼承至ImageView,所以FloatingActionButton擁有ImageView的所有屬性。本文講解的是一個實現了
介面的使用—自定義view點選事件的介面回撥
我這裡使用的是自定義組合view點選事件的介面回撥,底層還是呼叫的android原生的OnClickListener事件。效果圖: 三步實現自定義view點選事件的介面回撥。
Qt-Qlabel 自定義滑鼠點選事件以及文字樣式效果設計
最近專案中需要實現一個訊息推送的功能,模仿QQ訊息彈窗的方式實現,介面開發的工具為Qt。對桌面應用開發這塊不太熟悉,通過摸索嘗試也算是把這個功能實現了,其中也碰到了一些比較麻煩的問題,這些問題我看也具有一定的普遍性,就把我摸索出來的解決方法和大家分享下(可能有更好的方法我沒
ListView中自定義Item點選事件處理
開發中很常見的一個問題,專案中的listview不僅僅是簡單的文字,常常需要自己定義listview的Item,自己的Adapter去繼承BaseAdapter,在adapter中按照需求進行編寫,問題就出現了,可能會發生點選每一個item的時候沒有反應,無法獲
在螢幕上新增一個可移動,可點選的懸浮按鈕。
需求: 1.隨手指移動。 2.可觸發點選事件 實現步驟 在Android Studio的Gradle中匯入CircularFloatingActionMenu dependencies { compile 'com.oguzdev:Ci
ios 星星評分(支援點選和滑動)
思路:ios 中 touchesBegan和touchesMoved兩個方法可以獲取到UIView上的點選的座標和滑動的座標,根據座標,位於X座標左邊的imageview設定為“button_star_red”,右邊的設定為“button_star_red”。
Android 通過ViewPager實現點選和滑動切換Fragment標籤頁
如上圖效果,要切換 Fragment 標籤頁,可以通過點選標籤或者滑動標籤頁來實現。 網上應該有封裝好的開源庫可以直接利用,不過這裡介紹一下自己通過 ViewPager 實現該效果。 首先是佈局檔案: <?xml version="1.0" encodi