自定義控件 - Spinner

blog 中會記錄一些自定義控件的實現(xiàn)過程,完整的使用和其他的自定義控件可以參考倒谷,常用控件庫WidgetProductor整理

效果圖

Spinner.gif

默認item 布局文件item_choose_time

默認的item布局只有一個TextView,不過用戶可以通過實現(xiàn)實現(xiàn)BaseSpinnerAdapter 傳入自定義的布局。

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/choose_item"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:padding="10dp"
    android:textColor="@color/colorBlack"
    android:textSize="14sp" />

Spinner工具類

Spinner工具類接收上下文、一個TextView痰憎,一個BaseSpinnerAdapter的實現(xiàn)類;
如果不想使用系統(tǒng)的箭頭圖標攀涵,可以使用方法setArrows()設(shè)置铣耘。

/**
 * Spinner工具類
 * <p>
 * author: zuo
 * date: 2017/11/29 15:05
 */

public class SpinnerUtils {
    private Context mContext;
    private TextView mTextView;
    private BaseSpinnerAdapter mAdapter;
    private PopupWindow popupWindow;
    private boolean noData;
    @DrawableRes
    private int mArrowDown = R.drawable.arrow_down;
    @DrawableRes
    private int mArrowUp = R.drawable.arrow_top;

    public SpinnerUtils(Context context, TextView textView, @NonNull BaseSpinnerAdapter adapter) {
        this.mContext = context;
        this.mTextView = textView;
        this.mAdapter = adapter;
    }

    /**
     * 設(shè)置箭頭,如果不想使用系統(tǒng)的箭頭圖標以故,可以在這個方法中設(shè)置
     * 需要注意的時蜗细,這個方法需要在初始化方法init()之前調(diào)用
     * @param arrowDown
     * @param arrowUp
     */
    public void setArrows(@DrawableRes int arrowDown, @DrawableRes int arrowUp) {
        this.mArrowDown = arrowDown;
        this.mArrowUp = arrowUp;
    }

    public void init() {
        if (mAdapter != null && mAdapter.getData() != null && mAdapter.getData().size() > 0) {
            mTextView.setText("----請選擇---");
            tvSetImg(mTextView, mArrowDown);
            noData = false;
        } else {
            mTextView.setText("----無數(shù)據(jù)---");
            noData = true;
        }
        mTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (noData) {
                    Toast.makeText(mContext, "無數(shù)據(jù)!", Toast.LENGTH_SHORT).show();
                    return;
                }
                showPopupWindow();
            }
        });
    }

    private void showPopupWindow() {
        tvSetImg(mTextView, mArrowUp);
        View view = LayoutInflater.from(mContext).inflate(R.layout.choose_pop_rv, null);
        popupWindow = new PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
        popupWindow.setTouchable(true);
        popupWindow.setBackgroundDrawable(mContext.getResources().getDrawable(R.drawable.shape_popup_view));
        popupWindow.showAsDropDown(mTextView);
        popupWindow.setOnDismissListener(new PopupDismissListener());
        RecyclerView recyclerView = view.findViewById(R.id.rv_choose_pop);
        recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
        recyclerView.setAdapter(mAdapter);
        recyclerView.addItemDecoration(new DividerItemDecoration(mContext, DividerItemDecoration.VERTICAL));
    }

    /**
     * 關(guān)閉popupWindow
     */
    public void closeSpinner() {
        if (popupWindow != null) {
            popupWindow.dismiss();
        }
    }

    /**
     * 彈窗消失的時候讓箭頭換回來
     */
    class PopupDismissListener implements PopupWindow.OnDismissListener {
        @Override
        public void onDismiss() {
            tvSetImg(mTextView, mArrowDown);
        }

    }

    /**
     * 設(shè)置textView右側(cè)的圖像
     *
     * @param textView
     * @param img
     */
    private void tvSetImg(TextView textView, int img) {
        Drawable nav_up = mContext.getResources().getDrawable(img);
        nav_up.setBounds(0, 0, nav_up.getMinimumWidth(), nav_up.getMinimumHeight());
        textView.setCompoundDrawables(null, null, nav_up, null);
    }

}

Spinner基礎(chǔ)AdapterBaseSpinnerAdapter

BaseSpinnerAdapter基礎(chǔ)的SpinnerAdapte,用戶使用Spinner想自定義Adapter時需要繼承這個類怒详。

/**
 * 封裝一個基礎(chǔ)的SpinnerAdapter炉媒,便于外界調(diào)用時進行擴展
 *
 * @author zuo
 * @date 2018/7/23 15:24
 */
public abstract class BaseSpinnerAdapter<T extends RecyclerView.ViewHolder, E> extends RecyclerView.Adapter<T> {
    private static OnItemClickListener onItemClickListener;

    public static interface OnItemClickListener<E> {
        void onItemClick(View view, int position);

        void onItemLongClick(View view, int position);
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        onItemClickListener = listener;
    }

    protected Context context;
    protected List<E> datas;
    protected int layoutID;

    public BaseSpinnerAdapter(Context context, List<E> datas, int layoutID) {
        this.context = context;
        this.datas = datas;
        this.layoutID = layoutID;
    }

    @NonNull
    @Override
    public T onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View itemView = View.inflate(context, layoutID, null);
        return getViewHolder(itemView);
    }

    @Override
    public int getItemCount() {
        return datas == null ? 0 : datas.size();
    }

    @Override
    public void onBindViewHolder(final T holder, final int position) {
        setValues(holder, position);
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (onItemClickListener != null) {
                    onItemClickListener.onItemClick(holder.itemView, position);
                }
            }
        });

        holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                if (onItemClickListener != null) {
                    onItemClickListener.onItemLongClick(holder.itemView, position);
                }
                return true;
            }
        });
    }

    /**
     * 返回viewholder
     *
     * @param itemView
     * @return
     */
    protected abstract T getViewHolder(View itemView);

    /**
     * 設(shè)置控件數(shù)據(jù)
     *
     * @param holder
     * @param position
     */
    protected abstract void setValues(T holder, int position);

    /**
     * 獲取Spinner展示的數(shù)據(jù)
     */
    protected abstract List<E> getData();
}

一個示例SpinnerAdapterSpinnerChooseAdapter

實現(xiàn)效果圖的Adapter

/**
 * 展示String類型數(shù)據(jù)的SpinnerAdapter
 *
 * @author zuo
 * @date 2017/11/23 15:25
 */

public class SpinnerChooseAdapter extends BaseSpinnerAdapter<RecyclerView.ViewHolder, String> {
    private List<String> mData;

    public SpinnerChooseAdapter(Context context, List<String> list, OnItemClickListener itemClickListener) {
        super(context, list, R.layout.item_choose_time);
        this.mData = list;
        setOnItemClickListener(itemClickListener);
    }

    @Override
    protected RecyclerView.ViewHolder getViewHolder(View itemView) {
        itemView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT));
        return new ItemViewHolder(itemView);
    }

    @Override
    protected void setValues(RecyclerView.ViewHolder holder, int position) {
        ((ItemViewHolder) holder).textView.setText(mData.get(position));
    }

    @Override
    protected List<String> getData() {
        return mData;
    }


    public class ItemViewHolder extends RecyclerView.ViewHolder {
        TextView textView;

        public ItemViewHolder(View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.choose_item);
        }
    }
}

控件使用

SpinnerActivity中的使用代碼

public class SpinnerActivity extends AppCompatActivity implements BaseSpinnerAdapter.OnItemClickListener {
    private List<String> list = new ArrayList<>();
    private TextView textView;
    private SpinnerUtils spinnerUtils;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_spinner);
        initData();
        initView();
    }

    private void initView() {
        textView = findViewById(R.id.show_spinner);
        SpinnerChooseAdapter chooseAdapter = new SpinnerChooseAdapter(this, list, this);
        spinnerUtils = new SpinnerUtils(this, textView, chooseAdapter);
        spinnerUtils.init();
    }

    private void initData() {
        list.add("戰(zhàn)爭女神");
        list.add("蒙多");
        list.add("德瑪西亞皇子");
        list.add("殤之木乃伊");
        list.add("狂戰(zhàn)士");
        list.add("布里茨克拉克");
        list.add("冰晶鳳凰 艾尼維亞");
        list.add("德邦總管");
        list.add("野獸之靈 烏迪爾 (德魯伊)");
        list.add("賽恩");
        list.add("詭術(shù)妖姬");
        list.add("永恒夢魘");
    }

    @Override
    public void onItemClick(View view, int position) {
        Toast.makeText(this, "點擊" + list.get(position), Toast.LENGTH_SHORT).show();
        textView.setText(list.get(position));
        if (spinnerUtils != null) {
            spinnerUtils.closeSpinner();
        }
    }

    @Override
    public void onItemLongClick(View view, int position) {
        Toast.makeText(this, "長按" + list.get(position), Toast.LENGTH_SHORT).show();
        textView.setText(list.get(position));
        if (spinnerUtils != null) {
            spinnerUtils.closeSpinner();
        }
    }

}

自定義的Adapter

假定我們需要展示UserBean類型的數(shù)據(jù)(其他實體類亦可),我們就需要自定義一個adapter昆烁,同樣需要繼承BaseSpinnerAdapter橱野。

/**
 * 展示UserBean類型數(shù)據(jù)的SpinnerAdapter
 *
 * @author zuo
 * @date 2017/11/23 15:25
 */

public class SpinnerUserChooseAdapter extends BaseSpinnerAdapter<RecyclerView.ViewHolder, SpinnerActivity.UserBean> {
    private List<SpinnerActivity.UserBean> mData;

    public SpinnerUserChooseAdapter(Context context, List<SpinnerActivity.UserBean> list, OnItemClickListener itemClickListener) {
        super(context, list, R.layout.item_choose_time);
        this.mData = list;
        setOnItemClickListener(itemClickListener);
    }

    @Override
    protected RecyclerView.ViewHolder getViewHolder(View itemView) {
        itemView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT));
        return new ItemViewHolder(itemView);
    }

    @Override
    protected void setValues(RecyclerView.ViewHolder holder, int position) {
        ((ItemViewHolder) holder).textView.setText(mData.get(position).getName());
    }

    @Override
    protected List<SpinnerActivity.UserBean> getData() {
        return mData;
    }


    public class ItemViewHolder extends RecyclerView.ViewHolder {
        TextView textView;

        public ItemViewHolder(View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.choose_item);
        }
    }
}

控件使用

public class SpinnerActivity extends AppCompatActivity implements BaseSpinnerAdapter.OnItemClickListener {
    private List<UserBean> list = new ArrayList<>();
    private TextView textView;
    private SpinnerUtils spinnerUtils;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_spinner);
        initData();
        initView();
    }

    private void initView() {
        textView = findViewById(R.id.show_spinner);
        SpinnerUserChooseAdapter chooseAdapter = new SpinnerUserChooseAdapter(this, list, this);
        spinnerUtils = new SpinnerUtils(this, textView, chooseAdapter);
        spinnerUtils.setArrows(R.drawable.arrow_down_app,R.drawable.arrow_up_app);
        spinnerUtils.init();
    }

    private void initData() {
        list.add(new UserBean(1,"戰(zhàn)爭女神"));
        list.add(new UserBean(2,"蒙多"));
        list.add(new UserBean(3,"德瑪西亞皇子"));
        list.add(new UserBean(4,"殤之木乃伊"));
        list.add(new UserBean(5,"狂戰(zhàn)士"));
        list.add(new UserBean(6,"布里茨克拉克"));
        list.add(new UserBean(7,"冰晶鳳凰 艾尼維亞"));
        list.add(new UserBean(8,"德邦總管"));
        list.add(new UserBean(9,"野獸之靈 烏迪爾 (德魯伊)"));
        list.add(new UserBean(10,"賽恩"));
        list.add(new UserBean(11,"詭術(shù)妖姬"));
        list.add(new UserBean(12,"永恒夢魘"));
    }

    @Override
    public void onItemClick(View view, int position) {
        Toast.makeText(this, "點擊" + list.get(position).getName(), Toast.LENGTH_SHORT).show();
        textView.setText(list.get(position).getName());
        if (spinnerUtils != null) {
            spinnerUtils.closeSpinner();
        }
    }

    @Override
    public void onItemLongClick(View view, int position) {
        Toast.makeText(this, "長按" + list.get(position).getName(), Toast.LENGTH_SHORT).show();
        textView.setText(list.get(position).getName());
        if (spinnerUtils != null) {
            spinnerUtils.closeSpinner();
        }
    }

    public class UserBean{
        private int id;
        private String name;

        public UserBean(int id, String name) {
            this.id = id;
            this.name = name;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}

其他代碼

  • popupWindow 的背景 shape
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/colorWhite" />
    <corners android:radius="2dp" />
    <padding android:bottom="5dp" android:left="10dp" android:right="10dp" android:top="5dp"/>
    <stroke android:color="@color/font_common_2" android:width="1dp"/>
</shape>
  • popupWindow使用的Recyclerview
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rv_choose_pop"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorWhite"/>
Spinner.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市善玫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌密强,老刑警劉巖茅郎,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異或渤,居然都是意外死亡系冗,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門薪鹦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來掌敬,“玉大人,你說我怎么就攤上這事池磁”己Γ” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵地熄,是天一觀的道長华临。 經(jīng)常有香客問我,道長端考,這世上最難降的妖魔是什么雅潭? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任揭厚,我火速辦了婚禮,結(jié)果婚禮上扶供,老公的妹妹穿的比我還像新娘筛圆。我一直安慰自己,他們只是感情好椿浓,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布太援。 她就那樣靜靜地躺著,像睡著了一般轰绵。 火紅的嫁衣襯著肌膚如雪粉寞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天左腔,我揣著相機與錄音唧垦,去河邊找鬼。 笑死液样,一個胖子當著我的面吹牛振亮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鞭莽,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼坊秸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了澎怒?” 一聲冷哼從身側(cè)響起褒搔,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎喷面,沒想到半個月后星瘾,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡惧辈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年琳状,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盒齿。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡念逞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出边翁,到底是詐尸還是另有隱情翎承,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布倒彰,位于F島的核電站审洞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜芒澜,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一仰剿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧痴晦,春花似錦南吮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至碧浊,卻和暖如春涂邀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背箱锐。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工比勉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人驹止。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓浩聋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親臊恋。 傳聞我的和親對象是個殘疾皇子衣洁,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,328評論 25 707
  • 內(nèi)容 抽屜菜單 ListView WebView SwitchButton 按鈕 點贊按鈕 進度條 TabLayo...
    小狼W閱讀 1,614評論 0 10
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點贊按鈕進度條TabLayout圖標下拉刷新...
    皇小弟閱讀 46,793評論 22 665
  • 說起聊齋,估計沒幾個人能想起《珠兒》抖仅。包括故事發(fā)生地的常州人坊夫,沒誰想著建一個珠兒故居。 蒲松齡是個成語批發(fā)商撤卢,而這...
    覺史氏閱讀 2,172評論 0 1
  • 年前與母親通電話践樱,她無意間說起她的收音機沒法充電了。這可怎么辦呢凸丸?如今收音機毫不起眼,在大部分家庭中早已絕跡袱院,但我...
    張永勝_永往直前閱讀 481評論 4 3