人間煙火氣,最撫凡人心。 — 網(wǎng)絡(luò)
寫在前面
2020大半年沒有更新,2021年初我又來了,回顧一下2020年,年初立下的Flag,也算是完成了幾個,1.多吃蔬菜水果;2.戒煙少喝酒;3.堅持讀書;4.體重回到了大學(xué)畢業(yè)時的水平。還有沒完成的Flag,1.當(dāng)工作任務(wù)很多的時候,沒有抱著學(xué)習(xí)的態(tài)度去工作,而是為了完成任務(wù)而工作,沒有收獲;2.沒有堅持運動;3.偶爾還是不能調(diào)整好心態(tài)。當(dāng)然還有其他的收獲,1.駕照到手;2.買新房,開始還房貸的生活;3.向心儀的女孩子攤牌成功;4.報自考專升本。
現(xiàn)在進入正題,之前做過一個項目,有注冊賬號的功能,但是注冊之前要通過滑塊驗證碼,先來說說設(shè)計原理,云端會返回給我兩張同高不同寬的圖片,一張是有滑動塊缺口的全部圖片,一張是滑動塊圖片,我只需要橫向滑動將橫坐標(biāo)傳回給云端進行驗證即可。這個設(shè)計思路清晰明了,下面來說一下我的實現(xiàn)思路,首先想到的就是自定義View,往自定義View中添加兩個ImageView用來顯示圖片內(nèi)容,其中顯示滑動塊圖片的ImageView可以通過觸摸事件進行橫向移動,起始位置為最左邊,通過調(diào)整顯示滑動塊圖片的ImageView的左邊距達到橫向移動的效果。
具體實現(xiàn)
創(chuàng)建SliderView繼承自FrameLayout,創(chuàng)建兩個ImageView,通過計算手指移動的距離調(diào)整橫向移動。
public class SliderView extends FrameLayout {
// 背景圖(固定的)
private ImageView mInvariableView;
// 滑動塊(可移動)
private ImageView mVariableView;
// 觸摸抬起事件監(jiān)聽器
private OnTouchUpListener mOnTouchUpListener;
// 滑動塊的左邊距
private int mVariableLeftMargin;
// 滑動塊可以滑動的范圍
private int mVariableDistance;
// 記下手指按下的位置
private int mDownX;
public SliderView(@NonNull Context context) {
this(context, null);
}
public SliderView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SliderView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public SliderView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
/**
* 初始化
* @param context
*/
private void init(Context context) {
// 創(chuàng)建背景圖控件
mInvariableView = new ImageView(context);
mInvariableView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
addView(mInvariableView);
// 創(chuàng)建滑動塊控件
mVariableView = new ImageView(context);
mVariableView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
addView(mVariableView);
}
/**
* 移動滑動塊
* 原理:通過設(shè)置滑動塊的左邊距達到移動的效果
* @param margin 左邊距
*/
private void move(int margin) {
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) mVariableView.getLayoutParams();
marginLayoutParams.leftMargin = margin;
mVariableView.setLayoutParams(marginLayoutParams);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 手下按下時,記錄當(dāng)前位置
mDownX = (int) event.getX();
// 同時記錄滑動塊的左邊距
mVariableLeftMargin = ((MarginLayoutParams) mVariableView.getLayoutParams()).leftMargin;
break;
case MotionEvent.ACTION_MOVE:
// 計算手指移動的距離
int distance = (int) (event.getX() - mDownX);
// 如果左邊距不為0,說明在此次ACTON_DOWN事件產(chǎn)生之前,滑動塊已經(jīng)不在初始位置,已經(jīng)滑動過一次或多次,
// 要將已產(chǎn)生的左邊距加上,避免丟失上一次滑動的距離,導(dǎo)致滑動塊從初始位置移動
if (mVariableLeftMargin != 0) distance = mVariableLeftMargin + distance;
// 避免手指移動的距離過大,滑動塊超出背景圖的范圍,保證滑動塊一直在背景圖內(nèi)移動
if (distance < 0) distance = 0;
else if (distance > mVariableDistance) distance = mVariableDistance;
// 開始移動滑動塊
move(distance);
break;
case MotionEvent.ACTION_UP:
if (mOnTouchUpListener == null)
throw new NullPointerException("You have to set the OnTouchUpListener!");
mOnTouchUpListener.onTouchUp(mVariableView.getLeft());
break;
default:
break;
}
return true;
}
/**
* 重置滑動塊位置
*/
public void reset() {
move(0);
}
/**
* 設(shè)置圖片
* @param invariableBitmap 固定的背景圖
* @param variableBitmap 滑動塊圖片
*/
public void setSliderBitmap(Bitmap invariableBitmap, Bitmap variableBitmap) {
mInvariableView.setImageBitmap(invariableBitmap);
mVariableView.setImageBitmap(variableBitmap);
// 計算滑動塊可以移動的范圍
mVariableDistance = invariableBitmap.getWidth() - variableBitmap.getWidth();
}
/**
* 設(shè)置觸摸抬起事件監(jiān)聽器
* @param listener 觸摸抬起事件監(jiān)聽器
*/
public void setOnTouchUpListener(OnTouchUpListener listener) {
mOnTouchUpListener = listener;
}
public interface OnTouchUpListener {
void onTouchUp(int x);
}
}
如何使用
下面通過一個Demo演示如何使用該自定義View。
1.創(chuàng)建布局
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.demo.slider.SliderView
android:id="@+id/view_slider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
2.創(chuàng)建Activity
新建Activity,重寫onCreate函數(shù),調(diào)用setContentView指定布局,初始化View并SliderView設(shè)置監(jiān)聽器和滑動塊圖片。
public class MainActivity extends AppCompatActivity implements SliderView.OnTouchUpListener {
private SliderView mSliderView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
// 這里涉及到內(nèi)部機密,就用兩個本地圖片代替了,放在drawable資源目錄下
Bitmap invariableBitmap = DrawableUtil.drawableToBitmap(getResources().getDrawable(R.drawable.invariable));
Bitmap variableBitmap = DrawableUtil.drawableToBitmap(getResources().getDrawable(R.drawable.variable));
mSliderView = findViewById(R.id.view_slider);
mSliderView.setOnTouchUpListener(this);
mSliderView.setSliderBitmap(invariableBitmap, variableBitmap);
}
@Override
public void onTouchUp(int x) {
// 在這個回調(diào)里將橫坐標(biāo)傳回給云端,進行驗證
Log.d(MainActivity.class.getSimpleName(), "onTouchUp : x = " + x);
}
}
運行效果如下:


最后
開篇既然說了關(guān)于個人,那就再說說吧,畢竟要有始有終,我對2020年整體是滿意的,2021年希望能把2020年沒有完成的Flag完成,賺錢多多,成長多多。2020因為疫情注定不平凡,2021年因為疫情或許不能回家過年,希望疫情早日過去,大家都能夠正常生活。