android 史上最简单的下拉选择菜单DropDownMenu 几行代码轻松搞定!

2023-11-10

这是我在CSDN上第一篇原创文章,趁着从上家公司离职去考驾照的这段日子,想通过写技术博客的方式,锻炼一下自己的语言表达能力,以及对之前工作的总结。废话不多说了,直接进入正题

先给客官来张效果图:


一、思路

下拉菜单首先让我想到了PopupWindow,PopupWindow上的contentView是一个半透明的LineaLayout,再上面是一个ListView。popupWindow的showAsDropDown方法可以让popupWindow显示到某个控件的下方,出现和隐藏的时候加个动画就行了。那么下拉菜单已经有了,接下来上部的菜单按钮要怎么实现呢,无非就是LinearLayout上有个TextView和View,文字右边的箭头可以调用TextView的方法画到右边,无非多加一个状态参数来判定到底是画哪个。好了,思路有了,接下来我们看是怎么实现的。

二、实现

首先我封装了一个PopupUtil类:

public class PopWinDownUtil {
    private Context context;
    private View contentView;
    private View relayView;
    private PopupWindow popupWindow;
    public PopWinDownUtil(Context context, View contentView, View relayView){
        this.context = context;
        this.contentView = contentView;
        this.relayView = relayView;
        init();
    }
    public void init(){
        //内容,高度,宽度
        popupWindow = new PopupWindow(contentView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, true);
        //动画效果
        popupWindow.setAnimationStyle(R.style.AnimationTopFade);
        //菜单背景色
        ColorDrawable dw = new ColorDrawable(Color.TRANSPARENT);
        popupWindow.setBackgroundDrawable(dw);
        popupWindow.setOutsideTouchable(true);
        //关闭事件
        popupWindow.setSoftInputMode(PopupWindow.INPUT_METHOD_NEEDED);
        popupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);

        popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                if(onDismissLisener != null){
                    onDismissLisener.onDismiss();
                }
            }
        });
    }
    public void show(){
        //显示位置
        popupWindow.showAsDropDown(relayView);
    }
    public void hide(){
        if(popupWindow != null && popupWindow.isShowing()){
            popupWindow.dismiss();
        }
    }

    private OnDismissLisener onDismissLisener;
    public void setOnDismissListener(OnDismissLisener onDismissLisener){
        this.onDismissLisener = onDismissLisener;
    }
    public interface OnDismissLisener{
        void onDismiss();
    }
    public boolean isShowing(){
        return popupWindow.isShowing();
    }
}
可以直接把contenView和显示到哪个控件下面的View传进来,添加了一个监听popupWindow窗口消失的回调接口,代码很简单就不多说了。

之后就该菜单按钮了,代码如下:

public class DropdownButton extends RelativeLayout implements Checkable, View.OnClickListener, PopWinDownUtil.OnDismissLisener, AdapterView.OnItemClickListener {
    /**
     * 菜单按钮文字内容
     */
    private TextView text;
    /**
     * 菜单按钮底部的提示条
     */
    private View bLine;
    private boolean isCheced;
    private PopWinDownUtil popWinDownUtil;
    private Context mContext;
    private DropDownAdapter adapter;
    /**
     * 传入的数据
     */
    private List<DropBean> drops;
    /**
     * 当前被选择的item位置
     */
    private int selectPosition;

    public DropdownButton(Context context) {
        this(context, null);
    }

    public DropdownButton(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    private void init(Context context) {
        mContext = context;
        //菜单按钮的布局
        View view =  LayoutInflater.from(getContext()).inflate(R.layout.dropdown_tab_button,this, true);
        text = (TextView) view.findViewById(R.id.textView);
        bLine = view.findViewById(R.id.bottomLine);
        //点击事件,点击外部区域隐藏popupWindow
        setOnClickListener(this);
    }
    /**
     * 添加数据,默认选择第一项
     * @param dropBeans
     */
    public void setData(List<DropBean> dropBeans){
        if(dropBeans == null || dropBeans.isEmpty()){
            return;
        }
        drops = dropBeans;
        drops.get(0).setChoiced(true);
        text.setText(drops.get(0).getName());
        selectPosition = 0;
        View view = LayoutInflater.from(mContext).inflate(R.layout.dropdown_content, null);
        view.findViewById(R.id.content).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                popWinDownUtil.hide();
            }
        });
        ListView listView = (ListView) view.findViewById(R.id.list);
        listView.setOnItemClickListener(this);

        adapter = new DropDownAdapter(drops,mContext);
        listView.setAdapter(adapter);

        popWinDownUtil = new PopWinDownUtil(mContext,view,this);
        popWinDownUtil.setOnDismissListener(this);
    }

    public void setText(CharSequence content) {
        text.setText(content);
    }
    /**
     * 根据传过来的参数改变状态
     * @param checked
     */
    @Override
    public void setChecked(boolean checked) {
        isCheced = checked;
        Drawable icon;
        if (checked) {
            icon = getResources().getDrawable(R.mipmap.ic_dropdown_actived);
            text.setTextColor(getResources().getColor(R.color.green));
            bLine.setVisibility(VISIBLE);
            popWinDownUtil.show();
        } else {
            icon = getResources().getDrawable(R.mipmap.ic_dropdown_normal);
            text.setTextColor(getResources().getColor(R.color.black));
            bLine.setVisibility(GONE);
            popWinDownUtil.hide();
        }
        //把箭头画到textView右边
        text.setCompoundDrawablesWithIntrinsicBounds(null, null, icon, null);
    }
    @Override
    public boolean isChecked() {
        return isCheced;
    }
    @Override
    public void toggle() {
        setChecked(!isCheced);
    }
    @Override
    public void onClick(View v) {
        setChecked(!isCheced);
    }

    @Override
    public void onDismiss() {
        setChecked(false);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        if(selectPosition == position){
            return;
        }
        drops.get(selectPosition).setChoiced(false);
        drops.get(position).setChoiced(true);
        text.setText(drops.get(position).getName());
        adapter.notifyDataSetChanged();
        selectPosition = position;
        popWinDownUtil.hide();
        if(onDropItemSelectListener != null){
            onDropItemSelectListener.onDropItemSelect(position);
        }
    }

    private OnDropItemSelectListener onDropItemSelectListener;
    public void setOnDropItemSelectListener(OnDropItemSelectListener onDropItemSelectListener){
        this.onDropItemSelectListener = onDropItemSelectListener;
    }
    public interface OnDropItemSelectListener{
        void onDropItemSelect(int Postion);
    }


    class DropDownAdapter extends BaseAdapter {
        private List<DropBean> drops;
        private Context context;
        public DropDownAdapter(List<DropBean> drops, Context context){
            this.drops = drops;
            this.context = context;
        }
        @Override
        public int getCount() {
            return drops.size();
        }
        @Override
        public Object getItem(int position) {
            return position;
        }
        @Override
        public long getItemId(int position) {
            return position;
        }
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder;
            if(convertView == null){
                holder = new ViewHolder();
                convertView = LayoutInflater.from(context).inflate(R.layout.dropdown_item,parent,false);
                holder.tv = (TextView) convertView.findViewById(R.id.name);
                holder.tig = (ImageView) convertView.findViewById(R.id.check);
                convertView.setTag(holder);
            }else{
                holder = (ViewHolder) convertView.getTag();
            }
            holder.tv.setText(drops.get(position).getName());
            if(drops.get(position).isChoiced()){
                holder.tig.setVisibility(VISIBLE);
            }else{
                holder.tig.setVisibility(GONE);
            }
            return convertView;
        }
        private class ViewHolder{
            TextView tv;
            ImageView tig;
        }
    }
}
逻辑并不难,重要的部分都有提示,还是要讲一下,控件用到的数据是我定义的一个DropBean集合,里边有要展示的文字内容和一个isChecked状态,用来决定该item是否已被选中。如果我们的列表数据是从服务端获取的,那么我们只要用个for循环赋值成DropBean集合就行了。然后直接调用菜单控件的setData就不用管了。

接下来是我的调用示例:

public class MainActivity extends AppCompatActivity {
    private DropdownButton dropdownButton1;
    private DropdownButton dropdownButton2;
    private DropdownButton dropdownButton3;
    private List<DropBean> times;
    private List<DropBean> types;
    private List<DropBean> names;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        dropdownButton1 = (DropdownButton) findViewById(R.id.time1);
        dropdownButton2 = (DropdownButton) findViewById(R.id.time2);
        dropdownButton3 = (DropdownButton) findViewById(R.id.time3);

        initSomeData();
        dropdownButton1.setData(times);
        dropdownButton2.setData(types);
        dropdownButton3.setData(names);
    }

    private void initSomeData() {
        times = new ArrayList<>();
        types = new ArrayList<>();
        names = new ArrayList<>();
        int mouth = 5;
        int year = 2016;
        times.add(new DropBean("全部时间"));

        times.add(new DropBean(year + "年 全年"));

        for (int i = 0; i < mouth; i++) {
            times.add(new DropBean(year + "年 " + (i + 1) + "月"));
        }

        types.add(new DropBean("交通"));
        types.add(new DropBean("饮食"));

        names.add(new DropBean("小张"));
        names.add(new DropBean("小王"));
        names.add(new DropBean("小c"));
        names.add(new DropBean("小刘"));
    }
}
代码很少,逻辑也很明确,这里就不多说了。对了我们的布局文件是这样的:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical">
        <com.example.cui.dropdownmenu.DropdownButton
            android:id="@+id/time1"
            android:layout_width="0px"
            android:layout_height="50dp"
            android:layout_weight="1"/>
        <View
            android:layout_width="2px"
            android:layout_height="40dp"
            android:background="#dfdfdf"/>
        <com.example.cui.dropdownmenu.DropdownButton
            android:id="@+id/time2"
            android:layout_width="0px"
            android:layout_height="50dp"
            android:layout_weight="1"/>
        <View
            android:layout_width="2px"
            android:layout_height="40dp"
            android:background="#dfdfdf"/>
        <com.example.cui.dropdownmenu.DropdownButton
            android:id="@+id/time3"
            android:layout_width="0px"
            android:layout_height="50dp"
            android:layout_weight="1"/>
    </LinearLayout>
    <View
        android:layout_width="match_parent"
        android:layout_height="2px"
        android:background="#dfdfdf"/>
</LinearLayout>

是不是很简单,需要几个就写几个。

dropdown_in.xml的代码:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:duration="100"
        android:zAdjustment="top"
        android:interpolator="@android:interpolator/decelerate_quad"
        android:fillAfter="true"
        android:fillBefore="true"
        android:fromXScale="100%"
        android:fromYScale="0%"
        android:pivotX="0"
        android:pivotY="0"
        android:toXScale="100%"
        android:toYScale="100%" />
</set>
dropdown_out.xml的代码:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:duration="100"
        android:interpolator="@android:interpolator/accelerate_quad"
        android:fillAfter="true"
        android:fillBefore="true"
        android:fromXScale="100%"
        android:fromYScale="100%"
        android:pivotX="0"
        android:pivotY="0"
        android:toXScale="100%"
        android:toYScale="0%" />
</set>


到此史上最简单的下拉菜单就打造完了!

点击下载源码



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

android 史上最简单的下拉选择菜单DropDownMenu 几行代码轻松搞定! 的相关文章

  • 计算Android设备的PPI

    如何计算 Android 设备的 PPI 最特别的是 Android 平板电脑 请注意 我想计算设备的 PPI 而不是 DPI 就像一二三一样简单 让我们来计算一下PPI to Nexus 5 例如 float LCD Diagonal 4
  • Android 在打开应用程序时会广播吗?

    例如 如果我想知道Youtube何时打开 是否有与之相关的广播 我当然知道我可以轮询 logcat 消息来检查活动 但我可以通过广播来做到这一点吗 因为它会少得多的耗电 此链接似乎表明这是不可能的 如何跟踪 Android 中的应用程序使用
  • 在android中通过BLE传输图像

    我使用以下代码传输 1 MB 的图像 如果在每个数据包之间实现线程延迟 则图像将成功传输 如果未设置线程延迟 则所有数据包均从BluetoothGattServer 发送 但BluetoothGattCallback 不会接收所有数据包 任
  • Android 上的 SVG 支持

    Android 支持 SVG 吗 有什么例子吗 最完整的答案是这样的 Android 2 x 默认浏览器本身不支持 SVG Android 3 默认浏览器支持 SVG 要将 SVG 支持添加到 2 x 版本的平台 您有两个基本选择 安装功能
  • Service 和 IntentService,运行从服务器轮询数据库值的服务哪个更好?

    我读过很多关于Service and IntentService 然而 当做出决定时 我没有足够的信心选择使用哪种类型来创建一个后台服务 该服务将在一定时间间隔内从数据库轮询数据 并在获得所需数据时停止它 因为数据代表请求的状态 例如 订购
  • Android 如何更改 OnTouchListener 上的按钮背景

    你好 我在 xml 中有一个按钮 我正在使用OnTouchListener在我的活动中获得button按下并释放 但问题是 当我按下按钮时背景颜色没有改变 当我延长可能的活动时OnClickListener背景正在改变 任何人都可以告诉我的
  • 如何在android上的python kivy中关闭应用程序后使服务继续工作

    我希望我的服务在关闭应用程序后继续工作 但我做不到 我听说我应该使用startForeground 但如何在Python中做到这一点呢 应用程序代码 from kivy app import App from kivy uix floatl
  • Android 服务是否有办法检测设备何时锁定?

    我有一个 Android 服务 我希望在设备锁定时执行操作 我想澄清一下 我对屏幕开 关状态不感兴趣 我知道如何使用带有 Intent ACTION USER PRESENT 和 KeyguardManager inKeyguardRest
  • 使用 gradlew assembleRelease 从 React Native 创建发布 apk 时出现错误

    我想发布 apk 但我收到错误 文件已存在 mkdir D mobile 它在 d 驱动器中生成名为 mobile 的文件 删除文件后 再次执行 gradlew assembleRelease 创建该文件并抛出错误 任务 app bundl
  • 如何检查用户在EditText中输入自己的电话号码?

    用户将在我的 Android 应用程序的注册页面上的编辑文本中输入手机号码 如何检查用户输入的是他 她的手机号码而不是其他人的 我试过这个 TelephonyManager tMgr TelephonyManager mAppContext
  • 如何检查 Android 中连接的 wifi 网络是否处于活动状态

    如何自动检查android中连接的WiFi网络上的互联网是否处于活动状态 我可以检查 wifi 是否已启用或 wifi 网络是否已连接 但我不确定如何检查互联网是否已连接 这可能吗 private boolean connectionAva
  • 如何在TableLayout中创建三列

    我正在开发一个使用的屏幕TableLayout 在这里我可以轻松创建两列 但我怎样才能创建三列呢 这里有一个例子
  • 画透明圆,外面填充

    我有一个地图视图 我想在其上画一个圆圈以聚焦于给定区域 但我希望圆圈倒转 也就是说 圆的内部不是被填充 而是透明的 其他所有部分都被填充 请参阅这张图片了解我的意思 http i imgur com zxIMZ png 上半部分显示了我可以
  • Android 相机未保存在特定文件夹 [MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA]

    当我在 Intent 中使用 MediaStore INTENT ACTION STILL IMAGE CAMERA 时遇到问题 相机正常启动 但它不会将文件保存在我的特定文件夹 photo 中 但是当我使用 MediaStore ACTI
  • 在游戏视图下添加 admob

    我一直试图将 admob 放在我的游戏视图下 这是我的代码 public class HoodStarGame extends AndroidApplication Override public void onCreate Bundle
  • 如何在android asynctask中使用inputstream作为参数?

    我正在制作一个 Android 应用程序来跟踪股票详细信息 我将通过 csv 雅虎财经 检索数据 据我所知 在android 4 0中 网络连接无法在主线程上完成 因此 我将使用 asynctask 来建立连接 但是 我在参数方面遇到了一些
  • Android:监听状态栏通知

    有没有办法在状态栏被下拉时监听通知 1 用于检测状态栏变化 您可以注册一个监听器来获取系统UI可见性变化的通知 因此 要在您的活动中注册侦听器 Detecting if the user swipe from the top down to
  • Android studio - 如何查找哪个库正在使用危险权限?

    我正在尝试将 apk 上传到 google play 商店 但令我惊讶的是 我正在使用以下权限 Your APK is using permissions that require a privacy policy android perm
  • Android:AsyncTask ProgressDialog 将不会在 ActivityGroup 中打开

    我试图在轮询我的服务器时打开一个进度对话框 该类是一个 ActivityGroup 因为它嵌套在选项卡栏中 为了将视图保持在框架内 需要 ActivityGroup 这是我的 ActivityGroup 类的声明 public class
  • Android 材料芯片组件崩溃应用程序。无法膨胀 xml

    Tried Chip来自两个支持库的组件 com google android support design 28 0 0 rc01和材料 com google android material material 1 0 0 rc01 堆栈

随机推荐