blog 中会记录一些自定义控件的实现过程,完整的使用和其他的自定义控件可以参考,常用控件库WidgetProductor整理
效果图

默认item 布局文件item_choose_time
默认的item布局只有一个TextView
,不过用户可以通过实现实现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
的实现类;
如果不想使用系统的箭头图标,可以使用方法setArrows()
设置。
/**
* 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;
}
/**
* 设置箭头,如果不想使用系统的箭头图标,可以在这个方法中设置
* 需要注意的时,这个方法需要在初始化方法init()之前调用
* @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("----无数据---");
noData = true;
}
mTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (noData) {
Toast.makeText(mContext, "无数据!", 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));
}
/**
* 关闭popupWindow
*/
public void closeSpinner() {
if (popupWindow != null) {
popupWindow.dismiss();
}
}
/**
* 弹窗消失的时候让箭头换回来
*/
class PopupDismissListener implements PopupWindow.OnDismissListener {
@Override
public void onDismiss() {
tvSetImg(mTextView, mArrowDown);
}
}
/**
* 设置textView右侧的图像
*
* @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基础AdapterBaseSpinnerAdapter
BaseSpinnerAdapter
基础的SpinnerAdapte,用户使用Spinner想自定义Adapter时需要继承这个类。
/**
* 封装一个基础的SpinnerAdapter,便于外界调用时进行扩展
*
* @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);
/**
* 设置控件数据
*
* @param holder
* @param position
*/
protected abstract void setValues(T holder, int position);
/**
* 获取Spinner展示的数据
*/
protected abstract List<E> getData();
}
一个示例SpinnerAdapterSpinnerChooseAdapter
实现效果图的Adapter
/**
* 展示String类型数据的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("战争女神");
list.add("蒙多");
list.add("德玛西亚皇子");
list.add("殇之木乃伊");
list.add("狂战士");
list.add("布里茨克拉克");
list.add("冰晶凤凰 艾尼维亚");
list.add("德邦总管");
list.add("野兽之灵 乌迪尔 (德鲁伊)");
list.add("赛恩");
list.add("诡术妖姬");
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类型的数据(其他实体类亦可),我们就需要自定义一个adapter,同样需要继承BaseSpinnerAdapter。
/**
* 展示UserBean类型数据的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,"战争女神"));
list.add(new UserBean(2,"蒙多"));
list.add(new UserBean(3,"德玛西亚皇子"));
list.add(new UserBean(4,"殇之木乃伊"));
list.add(new UserBean(5,"狂战士"));
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,"诡术妖姬"));
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"/>

网友评论