欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

自定义view 写一个popup view

程序员文章站 2022-06-24 10:07:16
目标:实现一个popup view 自动显示在点击的view的附近,且箭头一直指向该view的水平中心位置效果图:思路自定义view包含PopupWindow的实例 对该实例进行操控步骤1 创建popupview的布局popview_container.xml

目标:

实现一个popup view 自动显示在点击的view的附近,且箭头一直指向该view的水平中心位置

效果图:

自定义view 写一个popup view
自定义view 写一个popup view
自定义view 写一个popup view
自定义view 写一个popup view
自定义view 写一个popup view

思路

自定义view包含PopupWindow的实例 对该实例进行操控

步骤1 创建popupview的布局popview_container.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/arrow_icon_up"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginBottom="-13dp"
        android:src="@drawable/popup_arrow_up" />


    <LinearLayout
        android:id="@+id/content_container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingStart="7dp"
        android:paddingEnd="7dp"
        android:paddingTop="7dp"
        android:paddingBottom="7dp"
        android:background="#ccc"
        android:orientation="vertical">
    </LinearLayout>

    <ImageView
        android:id="@+id/arrow_icon_down"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="-13dp"
        android:src="@drawable/popup_arrow_down"
        android:visibility="gone" />

</LinearLayout>



使用到的两个箭头
popup_arrow_down.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="40dp"
    android:height="20dp"
    android:viewportWidth="40"
    android:viewportHeight="20">
  <path
      android:pathData="M0,0h40L20,20 0,0z"
      android:fillColor="#ccc"/>
</vector>

popup_arrow_up.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="40dp"
    android:height="20dp"
    android:viewportWidth="40"
    android:viewportHeight="20">
  <path
      android:pathData="M0,20h40L20,0 0,20z"
      android:fillColor="#ccc"/>
</vector>

步骤2 popupview item的布局popview_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:minWidth="480dp"
    android:layout_height="wrap_content"
    android:background="@drawable/popupview_item_bg"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="120dp"
        android:layout_gravity="center_vertical"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/popup_view_item_icon"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_gravity="center"
            android:layout_marginStart="35dp"
            android:src="@drawable/ic_delete" />

        <TextView
            android:id="@+id/popup_view_item_des"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical|start"
            android:layout_marginStart="35dp"
            android:layout_marginEnd="20dp"
            android:ellipsize="marquee"
            android:singleLine="true"/>
    </LinearLayout>

    <ImageView
        android:id="@+id/popup_view_item_divider"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#0f0" />
</LinearLayout>



其中会使用到几个图标和背景
ic_delete.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="50dp"
    android:height="50dp"
    android:viewportWidth="50"
    android:viewportHeight="50">
    <path
        android:fillColor="#0ff"
        android:pathData="M40.206,10.682L9.75,10.682c-0.962,0 -1.75,0.788 -1.75,1.75 0,0.963 0.788,1.75 1.75,1.75h1.444L11.194,38.95c0,3.15 2.538,5.688 5.689,5.688h16.234c3.15,0 5.689,-2.538 5.689,-5.688L38.806,14.183h1.444c0.962,0 1.75,-0.788 1.75,-1.75 -0.044,-0.963 -0.831,-1.75 -1.794,-1.75zM19.815,36.15c0,0.7 -0.57,1.27 -1.313,1.27 -0.744,0 -1.313,-0.57 -1.313,-1.313L17.189,21.228c0,-0.7 0.569,-1.27 1.313,-1.27s1.313,0.57 1.313,1.27v14.921zM26.291,36.15c0,0.7 -0.569,1.27 -1.313,1.27s-1.313,-0.57 -1.313,-1.313L23.665,21.228c0,-0.7 0.57,-1.27 1.313,-1.27 0.744,0 1.313,0.57 1.313,1.27v14.921zM32.767,36.15c0,0.7 -0.569,1.27 -1.313,1.27s-1.312,-0.57 -1.312,-1.313L30.142,21.228c0,-0.7 0.568,-1.27 1.312,-1.27 0.744,0 1.313,0.57 1.313,1.27v14.921zM31.542,7.75c0,-0.962 -0.788,-1.75 -1.75,-1.75h-9.627c-0.963,0 -1.75,0.788 -1.75,1.75L18.415,9.5h13.127L31.542,7.75z" />
</vector>

popupview_item_bg.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true"  android:drawable="@color/colorPrimary" />
    <!-- Non pressed, Non selected, Non focused and Non enabled -->
    <item android:state_pressed="false"  android:drawable="@color/transparent" />
</selector>

步骤3 创建PopupView java文件

步骤3.1在PopupView内部新建内部类PopupViewItem 用于创建PopupView的Item

package com.example.testfragment.ui.main;

import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;

import com.example.testfragment.R;
import com.example.testfragment.util.ViewUtil;

import java.util.List;

public class PopupView {
    private final Context mContext;
    private PopupWindow mPopup;
    private View mArrow;

    //构造函数
    public PopupView(Context context) {
        mContext = context;
    }

    public void dismissView() {
        if (mPopup == null) {
            return;
        }
        if (mPopup.isShowing()) {
            mPopup.dismiss();
        }
    }

    private void clear() {
        if (mPopup != null) {
            mPopup.dismiss();
            mPopup = null;
        }
    }

    private void initView(View view) {
        clear();
        mPopup = new PopupWindow(view, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT);
        mPopup.setOutsideTouchable(true);
        mPopup.setTouchable(true);
        mPopup.update();
        mPopup.setFocusable(true);
    }

    //默认以点击view的坐下侧为起点 显示popup
    public void showView(View anchor, List<PopupViewItem> itemList) {
        dismissView();
        if (mContext == null) {
            return;
        }
        if (mContext.getResources() == null) {
            return;
        }
        if (anchor == null) {
            return;
        }

        //start to layout the pop-up view and set its event
        View currentPopupLayoutView = View.inflate(mContext, R.layout.popview_container, null);
        LinearLayout container = currentPopupLayoutView.findViewById(R.id.content_container);
        if (container != null) {
            int i = 0;
            for (PopupViewItem item : itemList) {
                //the last item should not show the divider
                i++;
                addClickActionItem(container, item, i != itemList.size());
            }
        }

        int popupViewHeightWithAnchor = getPopupViewMeasuredHeightWithAnchor(anchor, currentPopupLayoutView);
        int popupViewHeight = getPopupViewMeasuredHeight(currentPopupLayoutView);
        int anchorViewHeight = getAnchorViewMeasuredHeight(anchor);
        if (anchorViewHeight <= 0 || popupViewHeight <= 0 || popupViewHeightWithAnchor <= 0) {
            return;
        }

        //如果Y方向 空间不够显示 需要在Y方向上反转显示方向
        boolean revert = needReverse(anchor, popupViewHeightWithAnchor);
        if (revert) {
            revertPopupView(currentPopupLayoutView);
        }else{
            mArrow = currentPopupLayoutView.findViewById(R.id.arrow_icon_up);
        }

        int anchorViewY = getAnchorViewY(anchor);
        int y = revert ? (anchorViewY - popupViewHeight) : (anchorViewY + anchorViewHeight);

        //默认 popup view的正中与Anchor的水平正中对齐
        //1.计算Anchor x的中心
        int anchorXCenter = getAnchorViewX(anchor) + anchor.getWidth() / 2;
        //2.获取popup view的宽度
        int popupViewWidth = getPopupViewMeasuredWidth(currentPopupLayoutView);
        int popupViewStartX = anchorXCenter - popupViewWidth / 2;

        //如果水平空间不够显示popup view(被强行挤到屏幕内) 需要挪动箭头图标 计算挪动图标的水平值x
        if (popupViewStartX < 0) {
            //向左移动箭头(参数<0)
            mArrow.setTranslationX(popupViewStartX);
        } else if ((popupViewStartX + popupViewWidth) > ViewUtil.getAppScreenWidth(mContext)) {
            //向右移动箭头(参数>0)
            mArrow.setTranslationX(popupViewStartX + popupViewWidth - ViewUtil.getAppScreenWidth(mContext));
        }

        initView(currentPopupLayoutView);
        showView(anchor, popupViewStartX, y);
    }


    private void showView(View anchor, int x, int y) {
        if (mPopup != null) {
            mPopup.showAtLocation(anchor, Gravity.TOP | Gravity.START, x, y);
        }
    }

    //在某个view显示popup view,其中某个view就是anchor
    //计算anchor+popup view的高度
    private int getPopupViewMeasuredHeightWithAnchor(View anchor, View currentPopupLayoutView) {
        if (anchor == null || currentPopupLayoutView == null) {
            return -1;
        }
        //need to call measure() to generate the width and height
        currentPopupLayoutView.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        return currentPopupLayoutView.getMeasuredHeight() + anchor.getMeasuredHeight();
    }

    /**
     * @param container     popup view
     * @param item          popup的item项
     * @param isShowDivider 是否显示divider
     */
    private void addClickActionItem(LinearLayout container, final PopupViewItem item, boolean isShowDivider) {
        if (item == null || container == null) {
            return;
        }
        final View inflateView = ViewUtil.addView(R.layout.popview_item, container);
        if (inflateView != null) {
            if (item.getMinWidth() > -1) {
                inflateView.setMinimumWidth(item.getMinWidth());
            }
            ImageView icon = inflateView.findViewById(R.id.popup_view_item_icon);
            icon.setImageDrawable(mContext.getResources().getDrawable(item.getIconResID(), mContext.getTheme()));
            TextView tvDes = inflateView.findViewById(R.id.popup_view_item_des);
            tvDes.setText(item.getDescriptionStr());
            inflateView.setOnClickListener(item.getOnClickListener());

            if (!isShowDivider) {
                View dividerView = inflateView.findViewById(R.id.popup_view_item_divider);
                dividerView.setVisibility(View.GONE);
            }
        }
    }

    //如果剩余空间不够显示popup,向上反转
    private boolean needReverse(View anchor, int popupViewHeightWithAnchor) {
        return mContext != null && popupViewHeightWithAnchor + getAnchorViewY(anchor) > ViewUtil.getAppScreenHeight(mContext);
    }

    private void revertPopupView(View popupView) {
        if (popupView != null) {
            View arrowUp = popupView.findViewById(R.id.arrow_icon_up);
            View arrowDown = popupView.findViewById(R.id.arrow_icon_down);
            if (arrowUp != null && arrowDown != null) {
                arrowUp.setVisibility(View.GONE);
                arrowDown.setVisibility(View.VISIBLE);
                mArrow = arrowDown;
            }
        }
    }

    //计算anchorView的X绝对坐标
    private int getAnchorViewX(View anchor) {
        int[] locationOnScreen = new int[2];
        anchor.getLocationOnScreen(locationOnScreen);
        return locationOnScreen[0];
    }

    //计算anchorView的Y绝对坐标
    private int getAnchorViewY(View anchor) {
        int[] locationOnScreen = new int[2];
        anchor.getLocationOnScreen(locationOnScreen);
        return locationOnScreen[1];
    }

    //计算anchorView的高度
    private int getAnchorViewMeasuredHeight(View anchor) {
        if (anchor == null) {
            return -1;
        }
        return anchor.getMeasuredHeight();
    }

    //计算PopupView的高度
    private int getPopupViewMeasuredHeight(View currentPopupLayoutView) {
        if (currentPopupLayoutView == null) {
            return -1;
        }
        //need to call measure() to generate the getMeasuredWidth
        int wrapContentSpec = View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
        currentPopupLayoutView.measure(wrapContentSpec, wrapContentSpec);
        return currentPopupLayoutView.getMeasuredHeight();
    }

    //计算PopupView的宽度
    private int getPopupViewMeasuredWidth(View currentPopupLayoutView) {
        if (currentPopupLayoutView == null) {
            return -1;
        }
        currentPopupLayoutView.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        return currentPopupLayoutView.getMeasuredWidth();
    }

    //内部类 用于创建item
    public static class PopupViewItem {
        private int iconResID;
        private String descriptionStr;
        private View.OnClickListener onClickListener;
        /**
         * if we have not set up this value, it will keep the default with set in R.layout.popview_item
         */
        private int minWidth = -1;

        public PopupViewItem(int iconResID, String descriptionStr, View.OnClickListener onClickListener) {
            this.iconResID = iconResID;
            this.descriptionStr = descriptionStr;
            this.onClickListener = onClickListener;
        }

        public PopupViewItem(int iconResID, String descriptionStr, View.OnClickListener onClickListener, int minWidth) {
            this.iconResID = iconResID;
            this.descriptionStr = descriptionStr;
            this.onClickListener = onClickListener;
            this.minWidth = minWidth;
        }

        int getIconResID() {
            return iconResID;
        }

        View.OnClickListener getOnClickListener() {
            return onClickListener;
        }

        String getDescriptionStr() {
            return descriptionStr;
        }

        int getMinWidth() {
            return minWidth;
        }
    }
}

步骤4 编写activity的布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <ImageView
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:layout_alignParentEnd="false"
        android:background="#0f0"
        android:onClick="showPopupView1"
        android:src="@drawable/ic_launcher" />

    <ImageView
        android:layout_width="150dp"
        android:layout_height="100dp"
        android:layout_alignParentEnd="true"
        android:background="#0f0"
        android:onClick="showPopupView2"
        android:src="@drawable/ic_launcher" />

    <ImageView
        android:layout_width="170dp"
        android:layout_height="200dp"
        android:layout_alignParentEnd="false"
        android:layout_alignParentBottom="true"
        android:background="#0f0"
        android:onClick="showPopupView3"
        android:scaleType="centerInside"
        android:src="@drawable/ic_launcher" />

    <ImageView
        android:layout_width="250dp"
        android:layout_height="150dp"
        android:layout_alignParentEnd="true"
        android:layout_alignParentBottom="true"
        android:background="#0f0"
        android:onClick="showPopupView4"
        android:src="@drawable/ic_launcher" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="#0f0"
        android:onClick="showPopupView5"
        android:src="@drawable/ic_launcher" />
</RelativeLayout>

步骤5 编写测试代码

package com.example.testfragment;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import com.example.testfragment.ui.main.PopupView;

import java.util.ArrayList;
import java.util.List;


public class MainActivity extends AppCompatActivity {
    Context context;

    private PopupView popup1;
    private PopupView popup2;
    private PopupView popup3;
    private PopupView popup4;
    private PopupView popup5;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
        context = this;
    }

    public void showPopupView1(View viewClicked) {
        popup1 = new PopupView(this);
        List<PopupView.PopupViewItem> itemList = new ArrayList<>();
        itemList.add(new PopupView.PopupViewItem(R.drawable.ic_launcher, "popup1_1", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, "item1 click", Toast.LENGTH_SHORT).show();
                    }
                });
                if (popup1 != null) {
                    popup1.dismissView();
                }
            }
        }));
        popup1.showView(viewClicked, itemList);
    }

    public void showPopupView2(View view) {
        popup2 = new PopupView(context);
        List<PopupView.PopupViewItem> itemList = new ArrayList<>();
        itemList.add(new PopupView.PopupViewItem(R.drawable.ic_delete, "popup2_1", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, "item1 click", Toast.LENGTH_SHORT).show();
                    }
                });
                if (popup2 != null) {
                    popup2.dismissView();
                }
            }
        }));
        itemList.add(new PopupView.PopupViewItem(R.drawable.ic_delete, "popup2_2", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, "item2 click", Toast.LENGTH_SHORT).show();
                    }
                });
                if (popup2 != null) {
                    popup2.dismissView();
                }
            }
        }));
        itemList.add(new PopupView.PopupViewItem(R.drawable.ic_delete, "popup2_3", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, "item3 click", Toast.LENGTH_SHORT).show();
                    }
                });
                if (popup2 != null) {
                    popup2.dismissView();
                }
            }
        }));
        popup2.showView(view, itemList);
    }

    public void showPopupView3(View view) {
        popup3 = new PopupView(context);
        List<PopupView.PopupViewItem> itemList = new ArrayList<>();
        itemList.add(new PopupView.PopupViewItem(R.drawable.ic_delete, "popup3_1", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, "item1 click", Toast.LENGTH_SHORT).show();
                    }
                });
                if (popup3 != null) {
                    popup3.dismissView();
                }
            }
        }));
        itemList.add(new PopupView.PopupViewItem(R.drawable.ic_delete, "popup3_2", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, "item2 click", Toast.LENGTH_SHORT).show();
                    }
                });
                if (popup3 != null) {
                    popup3.dismissView();
                }
            }
        }, 500));
        popup3.showView(view, itemList);
    }

    public void showPopupView4(View view) {
        popup4 = new PopupView(context);
        List<PopupView.PopupViewItem> itemList = new ArrayList<>();
        itemList.add(new PopupView.PopupViewItem(R.drawable.ic_delete, "popup4_1", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, "item1 click", Toast.LENGTH_SHORT).show();
                    }
                });
                if (popup4 != null) {
                    popup4.dismissView();
                }
            }
        }));
        itemList.add(new PopupView.PopupViewItem(R.drawable.ic_launcher, "popup4_2", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, "item2 click", Toast.LENGTH_SHORT).show();
                    }
                });
                if (popup4 != null) {
                    popup4.dismissView();
                }
            }
        }));
        popup4.showView(view, itemList);
    }

    public void showPopupView5(View view) {
        popup5 = new PopupView(context);
        List<PopupView.PopupViewItem> itemList = new ArrayList<>();
        itemList.add(new PopupView.PopupViewItem(R.drawable.ic_launcher, "popup5_1", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, "item1 click", Toast.LENGTH_SHORT).show();
                    }
                });
                if (popup5 != null) {
                    popup5.dismissView();
                }
            }
        }));
        itemList.add(new PopupView.PopupViewItem(R.drawable.ic_delete, "popup5_2", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, "item2 click", Toast.LENGTH_SHORT).show();
                    }
                });
                if (popup5 != null) {
                    popup5.dismissView();
                }
            }
        }));
        popup5.showView(view, itemList);
    }
}

本文地址:https://blog.csdn.net/u011109881/article/details/110670246