欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Android Checkbox的使用方法

 更新時(shí)間:2016年02月01日 08:49:02   作者:Ocean-藏心  
這篇文章主要為大家詳細(xì)介紹了Android Checkbox的使用方法,感興趣的小伙伴們可以參考一下

0和1是計(jì)算機(jī)的基礎(chǔ),數(shù)理邏輯中0和1代表兩種狀態(tài),真與假.0和1看似簡(jiǎn)單,其實(shí)變化無(wú)窮. 今天我就來(lái)聊聊android控件中擁有著0和1這種特性的魔力控件checkbox.

先來(lái)講講Checkbox的基本使用.在XML中定義.

<?xml version="1.0" encoding="utf-8"?>
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/cbx"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:checked="false" />

在Activity中使用

CheckBox cbx = (CheckBox) findViewById(R.id.cbx);
cbx.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
  @Override
  public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
   //do something
  }
});

很簡(jiǎn)單.要注意的是,CheckBox本身是一個(gè)視圖,是展示給用戶看的,因此我們要用數(shù)據(jù)來(lái)控制它的展示.所以,我們的CheckBox在Activity中要這么寫

boolean isChecked= false;
CheckBox cbx = (CheckBox) findViewById(R.id.cbx);
cbx.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
  @Override
  public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    if(isChecked){
      //do something
    }else{
      //do something else
    }
  }
});
cbx.setChecked(isChecked);

這樣,我們改變數(shù)據(jù)的時(shí)候,視圖的狀態(tài)就會(huì)跟著數(shù)據(jù)來(lái)做改變了.注意,監(jiān)聽器一定要這setChecked之前設(shè)置,這樣才能體現(xiàn)出來(lái)數(shù)據(jù)來(lái)控制視圖的展示.

單獨(dú)用CheckBox很easy,接下來(lái),復(fù)雜的情況來(lái)啦,CheckBox如何跟ListView/RecyclerView(以下簡(jiǎn)稱LV/RV)配合使用.這就不能簡(jiǎn)單的考慮問(wèn)題啦,要知道LV/RV中的視圖個(gè)數(shù)跟數(shù)據(jù)集的里面的數(shù)據(jù)并不一致,真正的視圖個(gè)數(shù)遠(yuǎn)小于數(shù)據(jù)集中數(shù)據(jù)項(xiàng)的個(gè)數(shù).因?yàn)槠聊簧显诹斜碇械囊晥D是可以復(fù)用的.由于LV/RV的復(fù)用機(jī)制,如果我們沒(méi)有用數(shù)據(jù)來(lái)控制CheckBox狀態(tài)的話,將會(huì)導(dǎo)致CheckBox的顯示在列表中錯(cuò)亂.比方說(shuō)你只對(duì)第一個(gè)Item中的CheckBox做了選中操作,當(dāng)列表向上滾動(dòng)的時(shí)候,你會(huì)發(fā)現(xiàn),下面的Item中居然也會(huì)有被選中的.當(dāng)然,我剛學(xué)Android時(shí)候也遇到過(guò)這種情況,問(wèn)題的關(guān)鍵就在于要用數(shù)據(jù)來(lái)控制視圖的顯示.因此在getView/onBindViewHolder中,我們應(yīng)該這么寫.

holder.cbx.setTag(item);
holder.cbx.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
  @Override
  public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    Item item =(Item) buttonView.getTag();
    if(isChecked){
      item.setCheckState(true);
      //do something
    }else{
      item.setCheckState(false);
      //do something else
    }
  }
});
cbx.setChecked(item.getCheckState());

這種方法基本正確,但是我們要額外的給每個(gè)數(shù)據(jù)項(xiàng)里面添加一個(gè)字段來(lái)記錄狀態(tài),這代價(jià)就有點(diǎn)大了.一是不必這么做,二是這會(huì)導(dǎo)致本地?cái)?shù)據(jù)結(jié)構(gòu)跟服務(wù)端結(jié)構(gòu)不一致.通常,列表中使用CheckBox的話,很明顯是把選中的item給記錄下來(lái),可以這么理解,選中的狀態(tài)是列表給的,而item本身應(yīng)該是無(wú)狀態(tài)的.那么,如果重構(gòu)我們的代碼呢,Android為我們提供了一種完美的數(shù)據(jù)結(jié)構(gòu)來(lái)解決這個(gè)問(wèn)題.你可以用SparseArray,也可以用SparseBooleanArray,我現(xiàn)在習(xí)慣使用SparseBooleanArray,ok,請(qǐng)看代碼

private class Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
  SparseBooleanArray mCheckStates=new SparseBooleanArray();
  @Override
  public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    //...
  }
  @Override
  public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    holder.cbx.setTag(position);
    holder.cbx.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
      @Override
      public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        int pos =(int)buttonView.getTag();
        if(isChecked){
          mCheckStates.put(pos,true);
          //do something
        }else{
          mCheckStates.delete(pos);
          //do something else
        }
      }
    });
    cbx.setChecked(mCheckStates.get(position,false));
  }
  @Override
  public int getItemCount() {
    //...
  }
}

這樣列表就能正常顯示了,而且在你選中CheckBox的時(shí)候,會(huì)自動(dòng)觸發(fā)onCheckedChanged來(lái)對(duì)mCheckStates來(lái)進(jìn)行更新.此時(shí),如果你想用程序來(lái)選中某個(gè)item的時(shí)候,那么直接這樣就行了.

mCheckStates.put(pos,true);
adapter.notifyDatasetChanged();

如果我們想要取出列表列中所有的數(shù)據(jù)項(xiàng),那么有了SparseBooleanArray,這個(gè)就非常好辦啦.

ArrayList<Item> selItems=new ArrayList<>();
for(int i=0;i < mCheckStates.size();i++){
  if(mCheckStates.valueAt(i)){
    selItems.add(allItems.get(mCheckStates.keyAt(i)));
  }
}

竟然是如此的節(jié)省空間和時(shí)間,這樣的代碼誰(shuí)不喜歡呢.但是,這還不完美. 由于CheckBox這個(gè)控件太容易變了,為什么這么說(shuō)呢,因?yàn)榫退隳惆阉O(shè)成disabled的話,它依然是可以點(diǎn)選的,它的onCheckedChanged依然會(huì)觸發(fā).那么我們?cè)撛趺崔k呢.程序員考慮問(wèn)題呢,一般都是先想最笨的方法啦,既然onCheckedChanged依然會(huì)觸發(fā),那我就在里面把buttonView再設(shè)置成!isCheck的不就行了嘛.

holder.cbx.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
  @Override
  public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    buttonView.setChecked(!isChecked);
    //...
  }
});

但是這么寫的話,就會(huì)調(diào)用buttonView的onCheckedChanged,其實(shí)buttonView就是外面的holder.cbx,這就會(huì)造成死循環(huán).因此我們?nèi)绻胏bx本身去改變狀態(tài)的話,那么一定要加鎖.

boolean lockState=false;
holder.cbx.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
  @Override
  public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
   if(lockState)return;
   //不然cbx改變狀態(tài).
   lockState=true;
    buttonView.setChecked(!isChecked);
    lockState=false;
    //...
  }
});

對(duì)cbx加鎖其實(shí)還是挺常用的,比方說(shuō)在onCheckedChanged中,你要發(fā)一個(gè)請(qǐng)求,而請(qǐng)求的結(jié)果反過(guò)來(lái)會(huì)更新這個(gè)cbx的選中狀態(tài),你就必須要用lockState來(lái)直接改變cbx的狀態(tài)了,以便于cbx的狀態(tài)跟mCheckStates里面的是一致的.

mada mada,還有一種情況,如果在onCheckedChanged的時(shí)候,isChecked跟mCheckStates.get(pos)一致的話,這會(huì)導(dǎo)致什么情況呢.

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
 int pos =(int)buttonView.getTag();
  if(isChecked){
 mCheckStates.put(pos,true);
   //do something
 }else{
   mCheckStates.delete(pos);
   //do something else
 }
}

這就會(huì)讓你的//do something做兩次,這么做就是沒(méi)有必要的啦,而且如果你的//do something是網(wǎng)絡(luò)請(qǐng)求的話,這樣就會(huì)導(dǎo)致更大問(wèn)題.所以,我們有必要對(duì)這種情況做過(guò)濾.

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
  if(lockState)return;
  int pos =(int)buttonView.getTag();
  if(mCheckStates.get(pos,false) == isChecked)return;
  if(isChecked){
   mCheckStates.put(pos,true);
   //do something
  }else{
   mCheckStates.delete(pos);
   //do something else
  }
}

好啦,如果你能將CheckBox跟SparseBooleanArray聯(lián)用,并且能考慮到加鎖和過(guò)濾重選的話,那么說(shuō)明你使用CheckBox的姿勢(shì)擺正了.但是,我要講的精彩的地方才剛剛開始.

一個(gè)列表僅僅能讓用戶上滾下滑,那是最簡(jiǎn)單的使用,通常,由于列表項(xiàng)過(guò)多,產(chǎn)品會(huì)給列表項(xiàng)添加篩選的功能,而通常我們做篩選,會(huì)考慮到使用Spinner來(lái)做,但是,有用android自身提供的Spinner擴(kuò)展性太差,而且長(zhǎng)得丑,往往導(dǎo)致大家一怒之下,棄而不用.我呢,就是這么干的.經(jīng)過(guò)本人的奇思妙想,本人終于找到了一種很巧妙的機(jī)制來(lái)很優(yōu)雅的實(shí)現(xiàn)列表的篩選.下面我就來(lái)給大家分享一下.

接下來(lái)清楚我們今天的另一位主角,那就是PopupWindow(介紹),我先介紹一下原理,首先給CheckBox設(shè)置setOnCheckedChangeListener,然后在onCheckedChanged里面,isChecked分支中彈出PopupWindow,!isChecked中,讀取Popupwindow中的結(jié)果,用新的篩選條件來(lái)更新列表.ok,上代碼:

MainActivity:

public class MainActivity extends AppCompatActivity {

  String[] filter_type_strs = {"音樂(lè)", "書籍", "電影"};
  CheckBox cbx;
  private boolean lockState=false;
  int current_filter_type=0;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    cbx = (CheckBox) findViewById(R.id.cbx);

    cbx.setText(filter_type_strs[current_filter_type]);
    cbx.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
      @Override
      public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if (lockState) return;
        try {
          if (isChecked) {
            //此處傳入了cbx做參數(shù)
            PopupWindow pw = new FilterLinePw(buttonView.getContext(), cbx, filter_type_strs);
            pw.showAsDropDown(cbx);
          } else {
            //此處的buttonView就是cbx
            Integer pos = (Integer) buttonView.getTag();
            if (pos == null || pos == -1) return;
            current_filter_type = pos;

            Toast.makeText(MainActivity.this, "搜索"+filter_type_strs[current_filter_type], Toast.LENGTH_SHORT).show();
          }
        } catch (NullPointerException e) {
          //以防萬(wàn)一
          lockState = true;
          buttonView.setChecked(!isChecked);
          lockState = false;
        }
      }
    });

  }
}

FilterLinePw:

public class FilterLinePw extends PopupWindow {
  RadioGroup radioGroup;
  CheckBox outCbx;

  //為動(dòng)態(tài)生成radioButton生成id
  int[] rbtIds = {0, 1, 2};

  public FilterLinePw(Context context, CheckBox outCbx, String[] items) {
    super(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    View contentview = LayoutInflater.from(context).inflate(R.layout.filter_line_popupwindow, null);
    setContentView(contentview);
    setFocusable(true);
    setOutsideTouchable(true);
    this.outCbx = outCbx;
    contentview.setOnKeyListener(new View.OnKeyListener() {
      @Override
      public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
          dismiss();
          return true;
        }
        return false;
      }
    });
    contentview.setFocusable(true); // 這個(gè)很重要
    contentview.setFocusableInTouchMode(true);
    contentview.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        dismiss();
      }
    });

    init(context, contentview,items);

  }

  private void init(Context context, View contentview, String[] items) {
    /**
     * 用傳入的篩選條件初始化UI
     */
    radioGroup = (RadioGroup) contentview.findViewById(R.id.filter_layout);
    radioGroup.clearCheck();
    if (items == null) return;
    for (int i = 0; i < items.length; i++) {

      RadioButton radioButton = (RadioButton) LayoutInflater.from(context).inflate(R.layout.line_popupwindow_rbt, null);
      radioButton.setId(rbtIds[i]);
      radioButton.setText(items[i]);

      radioGroup.addView(radioButton, -1, radioGroup.getLayoutParams());

      if (items[i].equals(outCbx.getText())) {
        outCbx.setTag(i);
        radioButton.setChecked(true);
      }

    }
    radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
      @Override
      public void onCheckedChanged(RadioGroup group, int checkedId) {
        dismiss();
      }
    });
  }

  //重點(diǎn)內(nèi)容,重寫dismiss();
  @Override
  public void dismiss() {
    if (outCbx != null && outCbx.isChecked()) {
      int id = radioGroup.getCheckedRadioButtonId();
      RadioButton rbt = (RadioButton) radioGroup.findViewById(id);
      Integer old_tag = (Integer) outCbx.getTag();
      if (old_tag == null) {
        super.dismiss();
        return;
      }
      if (old_tag != id) {
        outCbx.setTag(id);
        outCbx.setText(rbt.getText());
      } else {
        outCbx.setTag(-1);
      }
      //下面執(zhí)行之后,會(huì)執(zhí)行MainActivity中的onCheckedChanged里的否定分支
      outCbx.setChecked(false);
    }
    super.dismiss();
  }
}

效果圖:

簡(jiǎn)單解釋一下:其實(shí)重點(diǎn)在PopupWindow里面,MainActivity的CheckBox作為參數(shù)傳遞到了 PopupWindow里.首先,用戶點(diǎn)擊MainActivity的CheckBox,接著會(huì)執(zhí)行isChecked分支,這樣PopupWindow就展示給了用戶,這樣用戶操作的環(huán)境就到了PopupWindow里面,等用戶選擇好篩選條件后,PopupWindow就把篩選條件設(shè)給outCbx,然后改變outCbx狀態(tài),從而觸發(fā)MainActivity中onCheckedChanged中的否定分支,此時(shí)展示的是一個(gè)Toast,實(shí)際應(yīng)用中可以是一個(gè)網(wǎng)絡(luò)請(qǐng)求.同時(shí),由于PopupWindow的代碼并沒(méi)有阻塞操作,所以會(huì)接著執(zhí)行下一句 super.dismiss(),這樣你在MainActivity就不用擔(dān)心PopupWindow的關(guān)閉問(wèn)題啦.最后,在MainActivity中還加入了try-catch來(lái)以防萬(wàn)一,這種機(jī)制真是太神奇啦.這種機(jī)制把篩選操作從Activity中分離了出來(lái),以后我們寫篩選可以完全獨(dú)立于Activity啦,真的是一種很軟件工程的做法.

隨后我會(huì)把其他篩選的情況開源,但是最精妙的原理就在于這個(gè)簡(jiǎn)單的例子上.各位看完之后不妨親自動(dòng)手試試,感受一下.

好啦,精彩的地方講完了,是不是不過(guò)癮啊.好吧,最后,我再拿點(diǎn)私房菜出來(lái). CheckBox是繼承自TextView,很多時(shí)候,我們的CheckBox的button屬性設(shè)置的圖片都不大,這就導(dǎo)致點(diǎn)擊CheckBox的區(qū)域也小,因此,我們需要用到TouchDelegate來(lái)擴(kuò)大CheckBox的可點(diǎn)擊區(qū)域上代碼:

public class FrameLayoutCheckBox extends FrameLayout {
  CompoundButton cbx;

  public FrameLayoutCheckBox(Context context) {
    super(context);
  }

  public FrameLayoutCheckBox(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public FrameLayoutCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }

  private CheckBox findCheckBox(View view) {
    //無(wú)遞歸廣度優(yōu)先遍歷尋找CheckBox - -!我只是想重溫一下C
    ArrayList<View> views = new ArrayList<>();
    views.add(view);
    while (!views.isEmpty()) {
      View c = views.remove(0);
      if (c instanceof CheckBox) {
        return (CheckBox) c;
      } else if (c instanceof ViewGroup) {
        ViewGroup fa = (ViewGroup) c;
        for (int i = 0; i < fa.getChildCount(); i++) {
          views.add(fa.getChildAt(i));
        }
      }
    }
    return null;
  }

  @Override
  protected void onFinishInflate() {
    super.onFinishInflate();
    if (getChildCount() > 0) {
      View child = findCheckBox(this);
      if (child instanceof CompoundButton) cbx = (CompoundButton) child;
    }
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    if (cbx != null) {
      Rect bounds = new Rect(getPaddingLeft(), getPaddingTop(), getPaddingLeft() + getMeasuredWidth() + getPaddingRight(), getPaddingTop() + getMeasuredHeight() + getPaddingBottom());
      TouchDelegate delegate = new TouchDelegate(bounds, cbx);
      setTouchDelegate(delegate);
    }
  }
}

這個(gè)類可以當(dāng)成FrameLayout,我們可以把CheckBox放里面,然后CheckBox的點(diǎn)擊區(qū)域就是整個(gè)FrameLayout的區(qū)域啦.當(dāng)然這個(gè)類也適用于RadioButton,但是你不能放多個(gè)CompoundButton在里面。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。

相關(guān)文章

  • Android深色模式適配適配過(guò)程詳解

    Android深色模式適配適配過(guò)程詳解

    這篇文章主要介紹了Android深色模式適配適配過(guò)程,深色模式其實(shí)就是我們經(jīng)常講到的夜間模式,與白天模式我們需要的屏幕高亮不同,晚上更加需要的是柔和的光,有助于減少手機(jī)對(duì)眼睛的傷害
    2023-04-04
  • Android內(nèi)部存儲(chǔ)改變讀取權(quán)限的方法

    Android內(nèi)部存儲(chǔ)改變讀取權(quán)限的方法

    今天小編就為大家分享一篇Android內(nèi)部存儲(chǔ)改變讀取權(quán)限的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08
  • android獲取附近藍(lán)牙設(shè)備并計(jì)算距離的實(shí)例代碼

    android獲取附近藍(lán)牙設(shè)備并計(jì)算距離的實(shí)例代碼

    下面小編就為大家分享一篇android獲取附近藍(lán)牙設(shè)備并計(jì)算距離的實(shí)例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • 淺談Android PathMeasure詳解和應(yīng)用

    淺談Android PathMeasure詳解和應(yīng)用

    本篇文章主要介紹了淺談Android PathMeasure詳解和應(yīng)用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • Flutter 狀態(tài)管理的實(shí)現(xiàn)

    Flutter 狀態(tài)管理的實(shí)現(xiàn)

    這篇文章主要介紹了Flutter 狀態(tài)管理的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • Android仿支付寶微信支付密碼界面彈窗封裝dialog

    Android仿支付寶微信支付密碼界面彈窗封裝dialog

    這篇文章主要介紹了Android仿支付寶微信支付密碼界面彈窗封裝dialog的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-03-03
  • Android應(yīng)用實(shí)現(xiàn)點(diǎn)擊按鈕震動(dòng)

    Android應(yīng)用實(shí)現(xiàn)點(diǎn)擊按鈕震動(dòng)

    這篇文章主要為大家詳細(xì)介紹了Android應(yīng)用實(shí)現(xiàn)點(diǎn)擊按鈕震動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • Opengl?ES之紋理貼圖使用示例

    Opengl?ES之紋理貼圖使用示例

    這篇文章主要為大家介紹了Opengl?ES之紋理貼圖使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • Android自定義View實(shí)現(xiàn)角度選擇器

    Android自定義View實(shí)現(xiàn)角度選擇器

    前幾天在Google Photos查看照片,用了一下它的圖片剪裁功能,于是我馬上就被其界面和操作吸引。后來(lái)想模仿做一個(gè)和Google Photos裁圖頁(yè)面幾乎一模一樣的角度選擇器,本文比較基礎(chǔ),在閱讀本文前只需要掌握最基礎(chǔ)的自定義View知識(shí)和Android事件知識(shí)。下面來(lái)一起學(xué)習(xí)下吧。
    2016-11-11
  • Android利用ViewDragHelper輕松實(shí)現(xiàn)拼圖游戲的示例

    Android利用ViewDragHelper輕松實(shí)現(xiàn)拼圖游戲的示例

    本篇文章主要介紹了Android利用ViewDragHelper輕松實(shí)現(xiàn)拼圖游戲的示例,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2017-11-11

最新評(píng)論