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