<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 1. 圖片資源: ![](https://img.kancloud.cn/41/2b/412ba42096c55f3fb9d4633d8963cb48_114x66.png) ![](https://img.kancloud.cn/0f/a0/0fa0375e59f07ddd5536f52ca1ccc91e_180x66.png) # 2. 簡單嘗試 由于比較簡單,所以這里就直接開始。 首先需要將兩個圖片給相對位置布局放置好,還是放置在xml文件中: ~~~ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <ImageView android:id="@+id/btn_b" android:background="@drawable/switch_background" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" /> <ImageView android:id="@+id/btn_f" android:background="@drawable/slide_button" android:layout_width="wrap_content" android:layout_alignBottom="@id/btn_b" android:layout_height="wrap_content" android:text="Hello World!" /> </RelativeLayout> ~~~ 然后可以看見預覽: ![](https://img.kancloud.cn/d2/d3/d2d38445453272704df24a628c73f80f_162x71.png) 接著,我們需要在代碼中進行簡單切換處理: ~~~ fun calcMargin(): Int{ return btn_b.width - btn_f.width } ~~~ ~~~ // 為背景層設置點擊事件 btn_b.setOnClickListener(object : View.OnClickListener { override fun onClick(v: View?) { val layoutParams = RelativeLayout.LayoutParams(btn_f.width, btn_f.height) if (isOpen) { layoutParams.marginStart = 0 } else { layoutParams.marginStart = calcMargin() } isOpen = !isOpen btn_f.layoutParams = layoutParams } }) ~~~ 然后就可以實現點擊切換。但是有時候用戶也會使用拖拽,所以這里還需要簡單處理一下拖拽的效果。但是這種處理還會隨著外層布局的改變而改變,也就是每次我們都需要自己去處理控件的邊距,或許要做到動態效果還需要進行添加動畫。比如下面的簡單處理: ~~~ class MainActivity : AppCompatActivity() { val gb by lazy { findViewById<ImageView>(R.id.btn_b)} val qg by lazy { findViewById<ImageView>(R.id.btn_f)} var startX = 0f var marginLeftValue = 0f var margin = 0f var isOpen = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_other) qg.setOnTouchListener(object : View.OnTouchListener { override fun onTouch(v: View?, event: MotionEvent?): Boolean { margin = (gb.width - qg.width).toFloat() when (event?.action) { MotionEvent.ACTION_DOWN -> { // 記錄起始位置 startX = event.x } MotionEvent.ACTION_MOVE -> { // 計算邏輯位置 val offset = event.x - startX marginLeftValue += offset // 屏蔽邊界 if (marginLeftValue < 0) { marginLeftValue = 0f } else if (marginLeftValue > margin) { marginLeftValue = margin } refresh() } } return true } }) } fun refresh(){ val parms = RelativeLayout.LayoutParams(qg.width, qg.height) parms.marginStart = marginLeftValue.toInt() qg.layoutParams = parms } } ~~~ 但是這不是我們所期望的,這里將其封裝一下。以方便調用。 # 3. 回顧 就需要自定View,當然對于自定義的屬性這里再次復習一下: * 定義一個繼承自View的類,并復寫對應的構造方法; * 在res/values/attrs.xml文件中定義需要用的屬性; * 在主布局文件中使用; * 在自定義的View類中寫自己需要的一些操作功能; 當然,這里簡單復寫一下,就以測試為主。比如首先定義對應的attrs.xml文件: ~~~ <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="MyButton"> <attr name="textInfo" format="string"/> <attr name="textSize" format="integer"/> <attr name="background" format="reference|color"/> </declare-styleable> </resources> ~~~ 對應的在主布局文件中使用: ~~~ <com.weizu.switchbutton.MyButton android:layout_width="wrap_content" android:layout_height="wrap_content" app:textInfo="Hello!" app:textSize="32" app:textBg="#FF00FF" /> ~~~ 然后我們再看下自定義的View,這里由于我只需要xml方式,所以這里就不需要其余的構造函數,只需要一個即可。這里首先來看看使用命名空間方式獲取配置的屬性值: ## 3.1 命名空間方式獲取屬性值 ~~~ class MyButton: View{ // 讀取xml中配置的屬性 constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs){ // 方法一,直接獲取字符串類型數據 val textInfo = attrs?.getAttributeValue("http://schemas.android.com/apk/res-auto", "textInfo") val textSize = attrs?.getAttributeValue("http://schemas.android.com/apk/res-auto", "textSize") val background = attrs?.getAttributeValue("http://schemas.android.com/apk/res-auto", "textBg") Log.e("TAG", "textInfo: ${textInfo}, textSize: ${textSize}, textBg: ${background}") } } ~~~ 運行后可以看見日志信息: > textInfo: Hello!, textSize: 32, textBg: #ffff00ff 值得注意的是,對于背景資源這里是引用,但是這種按照命名空間取出值的方式得到的還是一個字符串,比如如果我們傳入的是一個資源ID,再次測試一下,修改下主布局文件: ~~~ <com.weizu.switchbutton.MyButton android:layout_width="wrap_content" android:layout_height="wrap_content" app:textInfo="Hello!" app:textSize="32" app:textBg="@drawable/slide_button" /> ~~~ 然后再次運行: > textInfo: Hello!, textSize: 32, textBg: @2131165328 從上面結果可以看出對于引用類型資源,就不適用了,因為返回的也只是一個資源ID,沒多大意義。 ## 3.2 TypedArray ~~~ class MyButton: View{ // 讀取xml中配置的屬性 constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { // 方法二,TypedArray val typedArray = context?.obtainStyledAttributes(attrs, R.styleable.MyButton) for (i in 0..typedArray!!.indexCount) { val index = typedArray.getIndex(i) when (index) { R.styleable.MyButton_textInfo -> { Log.e("TAG", "MyButton_textInfo: ${typedArray.getText(index)}") } R.styleable.MyButton_textSize -> { Log.e("TAG", "MyButton_textInfo: ${typedArray.getInt(index, 0)}") } R.styleable.MyButton_textBg -> { Log.e("TAG", "MyButton_textInfo: ${typedArray.getDrawable(index)}") } } } } } ~~~ 當然需要回收一下資源: ~~~cpp //釋放TypedArray typedArray.recycle(); ~~~ # 4. 封裝 回到正題,這里簡單分析可以知道我們需要定義的只有三個屬性: * 前景圖層資源 * 背景圖層資源 * 滑塊拖動時間 ## 4.1 預備工作 ~~~ res/values/attrs.xml <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="MyButton"> <attr name="bgImg" format="reference"/> <attr name="qgImg" format="reference"/> <attr name="DTime" format="integer"/> </declare-styleable> </resources> ~~~ ~~~ main_activity.xml <com.weizu.switchbutton.MyButton android:layout_width="wrap_content" android:layout_height="wrap_content" app:bgImg="@drawable/switch_background" app:qgImg="@drawable/slide_button" app:DTime="500" /> ~~~ 然后我們就可以簡單的讓其繪制出來: ~~~ class MyButton(context: Context?, attrs: AttributeSet?) : View(context, attrs) { lateinit var mQg: Bitmap lateinit var mBg: Bitmap lateinit var paint: Paint var mDTime: Int = 0 var margin: Int = 0 init { // 方法二,TypedArray// 讀取xml中配置的屬性 val typedArray = context?.obtainStyledAttributes(attrs, R.styleable.MyButton) for (i in 0..typedArray!!.indexCount) { val index = typedArray.getIndex(i) when (index) { R.styleable.MyButton_qgImg -> { mQg = (typedArray.getDrawable(index)!! as BitmapDrawable).bitmap } R.styleable.MyButton_bgImg -> { mBg = (typedArray.getDrawable(index)!! as BitmapDrawable).bitmap } R.styleable.MyButton_DTime -> { mDTime = typedArray.getInt(index, mDTime) } } } // 計算距離 margin = mBg.width - mQg.width // 初始化畫筆 paint = Paint() paint.isAntiAlias = true } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { // 設置寬度為背景圖片的寬度 setMeasuredDimension(mBg.width, mBg.height) } override fun onDraw(canvas: Canvas?) { // 繪制前景和背景 canvas?.drawBitmap(mBg, 0f, 0f, paint) canvas?.drawBitmap(mQg, 0f, 0f, paint) } } ~~~ 效果: ![](https://img.kancloud.cn/36/7a/367a141134998f152eb170d825ca7052_227x70.png) 然后就是為其添加動態的交互效果。 ## 4.2 添加點擊 類似的,我們可以很容易的為之添加點擊事件,因為這里是整個view,所以我們可以在其中直接設置監聽即可。首先定義兩個狀態: ~~~ // 開關狀態 var isOpen = false // 前景層距離左邊界的值 var marginLeftValue: Float ~~~ 然后修改onDraw: ~~~ override fun onDraw(canvas: Canvas?) { // 繪制前景和背景 canvas?.drawBitmap(mBg, 0f, 0f, paint) canvas?.drawBitmap(mQg, marginLeftValue, 0f, paint) } ~~~ 然后設置一下監聽: ~~~ // 設置點擊監聽 setOnClickListener(object : OnClickListener{ override fun onClick(v: View?) { if(isOpen){ // 設置邊距為最大,然后重新繪制 marginLeftValue = margin.toFloat() } else{ // 設置邊距為0,然后重新繪制 marginLeftValue = 0f } isOpen = !isOpen postInvalidate() } }) ~~~ 就可以做到按鈕的簡單開關切換。 ## 4.3 添加拖拽事件 對于拖拽事件,我們需要使用到觸摸事件,所以這里需要重寫一下onTouchEvent方法,然后處理的簡單邏輯為: * 按下記錄位置,然后根據移動的下一個點位置來判斷移動方向; * 根據手指移動距離來計相應的移動前景照片; * 設置有效移動范圍; * 判斷開關狀態,然后重新繪制; ~~~ // 觸摸事件 override fun onTouchEvent(event: MotionEvent?): Boolean { super.onTouchEvent(event) when(event?.action){ MotionEvent.ACTION_DOWN -> { // 手指按下,記錄起始值 currentX = event.x } MotionEvent.ACTION_MOVE -> { // 手指移動,計算偏移量 val endX = event.x val offset = endX - currentX // 對應邏輯上移動的距離 marginLeftValue += offset // 但是要屏蔽非法值 if(marginLeftValue < 0){ marginLeftValue = 0f } else if(marginLeftValue > margin){ marginLeftValue = margin.toFloat() } // 請求重新繪制 postInvalidate() // 更新 currentX = endX } MotionEvent.ACTION_UP -> { // 手指離開屏幕,判斷當前狀態 isOpen = marginLeftValue > margin / 2 if(isOpen){ // 設置邊距為最大,然后重新繪制 marginLeftValue = margin.toFloat() } else{ // 設置邊距為0,然后重新繪制 marginLeftValue = 0f } postInvalidate() } } return true; // 表示事件已經處理 } ~~~ 然后就可以實現這個效果。至于拖動時間這里感覺沒有這個必要。 # 5. 完整代碼: ~~~ class MyButton(context: Context?, attrs: AttributeSet?) : View(context, attrs) { lateinit var mQg: Bitmap lateinit var mBg: Bitmap var paint: Paint var mDTime: Int = 0 var margin: Int = 0 // 開關狀態 var isOpen = false // 前景層距離左邊界的值 var marginLeftValue: Float init { // 初始化邊距值為0 marginLeftValue = 0f // 方法二,TypedArray// 讀取xml中配置的屬性 val typedArray = context?.obtainStyledAttributes(attrs, R.styleable.MyButton) for (i in 0..typedArray!!.indexCount) { val index = typedArray.getIndex(i) when (index) { R.styleable.MyButton_qgImg -> { mQg = (typedArray.getDrawable(index)!! as BitmapDrawable).bitmap } R.styleable.MyButton_bgImg -> { mBg = (typedArray.getDrawable(index)!! as BitmapDrawable).bitmap } R.styleable.MyButton_DTime -> { mDTime = typedArray.getInt(index, mDTime) } } } // 計算距離 margin = mBg.width - mQg.width // 初始化畫筆 paint = Paint() paint.isAntiAlias = true // 設置點擊監聽 setOnClickListener(object : OnClickListener{ override fun onClick(v: View?) { if(isOpen){ // 設置邊距為最大,然后重新繪制 marginLeftValue = margin.toFloat() } else{ // 設置邊距為0,然后重新繪制 marginLeftValue = 0f } isOpen = !isOpen postInvalidate() } }) } var currentX = 0f // 觸摸事件 override fun onTouchEvent(event: MotionEvent?): Boolean { super.onTouchEvent(event) when(event?.action){ MotionEvent.ACTION_DOWN -> { // 手指按下,記錄起始值 currentX = event.x } MotionEvent.ACTION_MOVE -> { // 手指移動,計算偏移量 val endX = event.x val offset = endX - currentX // 對應邏輯上移動的距離 marginLeftValue += offset // 但是要屏蔽非法值 if(marginLeftValue < 0){ marginLeftValue = 0f } else if(marginLeftValue > margin){ marginLeftValue = margin.toFloat() } // 請求重新繪制 postInvalidate() // 更新 currentX = endX } MotionEvent.ACTION_UP -> { // 手指離開屏幕,判斷當前狀態 isOpen = marginLeftValue > margin / 2 if(isOpen){ // 設置邊距為最大,然后重新繪制 marginLeftValue = margin.toFloat() } else{ // 設置邊距為0,然后重新繪制 marginLeftValue = 0f } postInvalidate() } } return true; // 表示事件已經處理 } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { // 設置寬度為背景圖片的寬度 setMeasuredDimension(mBg.width, mBg.height) } override fun onDraw(canvas: Canvas?) { // 繪制前景和背景 canvas?.drawBitmap(mBg, 0f, 0f, paint) canvas?.drawBitmap(mQg, marginLeftValue, 0f, paint) } } ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看