Android基础之FrameLayout的再认识——模拟PopupWindow弹出框效果

FrameLayout即帧布局,以往开发的时候用的比较少,但最近做到一些弹出框的效果,就不得不使用帧布局来为底层的View来遮盖,中层用透明的view,最后再在上层铺弹出的View来达到既定的效果。

分析一下如下的一张效果图:

        

        该View就是由 底层为粉色 覆盖上一层半透明view 然后再在上层添加一层白色背景的效果图。

以此是否可以模拟顶部弹出的弹出框呢。

public class CustomFrame extends LinearLayout{
    /**
     *  承载内容的主体view
     * */
    private FrameLayout mFrameContent;
    private Context mContext;
    //遮罩颜色
    private int maskColor = 0x88888888;
    public CustomFrame(Context context) {
        this(context,null);
    }

    public CustomFrame(Context context, @Nullable AttributeSet attrs) {
        this(context,attrs,0);
    }

    public CustomFrame(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        init(context,attrs);
    }
    
    /**
     *   初始化View :
     *   这里通过java代码,添加入内,并没有使用什么xml布局
     * */
    private void init(Context context, AttributeSet attrs) {
        // 创建布局
        mFrameContent = new FrameLayout(context);
        // 设置Frame的信息包 为铺满全屏 new xxx.LayoutParams(x,y)
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
        // 将信息包设置给view
        mFrameContent.setLayoutParams(params);
        // 将View添加给父布局
        addView(mFrameContent);
    }
    
    /**
     *  将底层 半透明层和上层添加入内
     * */
    public void setCustomFrameView(View beyondView,View topView){
        mFrameContent.addView(beyondView,0);
        
        View maskView = new View(mContext);
        maskView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
        maskView.setBackgroundColor(maskColor);
        mFrameContent.addView(maskView,1);
        
        mFrameContent.addView(topView);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.zxl.myworklearning.activities.views.activities.FrameCusActivity">
    <com.example.zxl.myworklearning.widget.CustomFrame
        android:id="@+id/customView"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </com.example.zxl.myworklearning.widget.CustomFrame>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:background="#fff"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="300dp">
        <TextView
            android:layout_marginTop="200dp"
            android:layout_marginLeft="100dp"
            android:text="FrameLayout透明度学习"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>
</LinearLayout>
public class FrameCusActivity extends AppCompatActivity {
    private CustomFrame mCustomFrame;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_frame_cus);
        mCustomFrame = (CustomFrame) findViewById(R.id.customView);
        View view = new View(this);
        view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        view.setBackgroundColor(ContextCompat.getColor(this,R.color.colorAccent));

        View inflate = LayoutInflater.from(this).inflate(R.layout.layout_top, null);
        mCustomFrame.setCustomFrameView(view,inflate);


    }
}

其实上面这些代码和在xml书写如下代码是等价的,只不过一个是自定义view一个是布局罢了,

但我们有必要学习动态添加代码并且设置相对应的属性等。而且抽离出来可以做自定义view大大提高了利用率。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.zxl.myworklearning.activities.views.activities.FrameTest2Activity">
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <RelativeLayout
            android:background="@color/colorAccent"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        </RelativeLayout>
        <View
            android:background="#88888888"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
        <LinearLayout
            android:background="#fff"
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="300dp">
            <TextView
                android:layout_marginTop="200dp"
                android:layout_marginLeft="100dp"
                android:text="FrameLayout透明度学习"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </FrameLayout>
</LinearLayout>


总之来说帧布局还是有很大的用处的。不仅仅是应用于Fragment中吧。

紧着着我又马不停蹄的对网上的一个开源代码DropDownView进行了修改,原因是将其放入相对布局中的话,主体显示的内容View会被顶出去,而我希望是像五八一样的透明的显示,嗯,就是这样,所以我进行了如下修改。先看下效果吧:


大致说明下:主要是运用了FrameLayout的思路进行布局的重构,通过动态的方式给主体的FrameLayout添加布局,来达到主体内容居于底部,中间层透明和上层弹出的效果,然后我也修改了动画,原因是过渡动画掌握的不好,后面会进行研究。下面贴出核心代码。

/**
 * @author zxl 修改版的DropDownView
 */

public class DropDownView extends RelativeLayout {
    private static final int DROP_DOWN_CONTAINER_MIN_DEFAULT_VIEWS = 1;
    private static final int DROP_DOWN_HEADER_CONTAINER_MIN_DEFAULT_VIEWS = 0;
    /**
     *  注解该View可以为空
     * */
    @Nullable
    private View expandedView;
    @Nullable
    private View headerView;
    private ViewGroup dropDownHeaderContainer;
    private LinearLayout dropDownContainer;
    private boolean isExpanded; 
    private DropDownListener dropDownListener;
    private int backgroundColor;
    private int overlayColor;
    private FrameLayout mFmContainer;
    private View mMaskView;

    public DropDownView(Context context) {
        super(context);
        init(context, null);
    }

    public DropDownView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

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

   /**
    *  目标 Api 21 Android 5.0
    * */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public DropDownView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs);
    }

    /**
     * @return the {@link DropDownListener} that was set by you. Default is null.
     * @see #setDropDownListener(DropDownListener)
     */
    @Nullable
    public DropDownListener getDropDownListener() {
        return dropDownListener;
    }

    /**
     * @param dropDownListener your implementation of {@link DropDownListener}.
     * @see DropDownListener
     */
    public void setDropDownListener(DropDownListener dropDownListener) {
        this.dropDownListener = dropDownListener;
    }

    /**
     * @return true if the view is expanded, false otherwise.
     */
    public boolean isExpanded() {
        return isExpanded;
    }

    /**
     * 
     * @param headerView your header view
     */
    public void setHeaderView(@NonNull View headerView) {
        this.headerView = headerView;
        /* ------ 移除所有头布局包含的控件 --- */
        if (dropDownHeaderContainer.getChildCount() > DROP_DOWN_HEADER_CONTAINER_MIN_DEFAULT_VIEWS) {
            for (int i = DROP_DOWN_HEADER_CONTAINER_MIN_DEFAULT_VIEWS; i < dropDownHeaderContainer.getChildCount(); i++) {
                dropDownHeaderContainer.removeViewAt(i);
            }
        }
        dropDownHeaderContainer.addView(headerView);
    }

    /**
     *  精华设置伸展的布局
     * @param expandedView your header view
     */
    
    public void setExpandedView(@NonNull View expandedView,@NonNull View beyondView) {
        this.expandedView = expandedView;
        if (mFmContainer.getChildCount() > DROP_DOWN_CONTAINER_MIN_DEFAULT_VIEWS) {
            for (int i = DROP_DOWN_CONTAINER_MIN_DEFAULT_VIEWS; i < dropDownContainer.getChildCount(); i++) {
                mFmContainer.removeViewAt(i);
            }
        }
        mFmContainer.addView(beyondView,0);
        mMaskView = new View(getContext());
        mMaskView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        mMaskView.setBackgroundColor(0x88888888);
        mFmContainer.addView(mMaskView,1);
        mMaskView.setOnClickListener(emptyDropDownSpaceClickListener);

        mFmContainer.addView(expandedView,2);
        mMaskView.setVisibility(isExpanded?View.VISIBLE:View.GONE);
        expandedView.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
    }

    /**
     *  伸展
     */
   
    public void expandDropDown() {
        if (!isExpanded && expandedView != null) {

            if (dropDownListener != null) {
                dropDownListener.onExpandDropDown();
            }
            expandedView.setVisibility(View.VISIBLE);
            expandedView.setAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.dd_menu_in));
            mMaskView.setVisibility(VISIBLE);
            mMaskView.setAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.dd_mask_in));
            isExpanded = true;
        }
    }
    
    /**
     *   缩起来了
     * */
    public void collapseDropDown() {
        if (isExpanded && expandedView != null) {
            expandedView.setVisibility(View.GONE);
            expandedView.setAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.dd_menu_out));
            mMaskView.setVisibility(GONE);
            mMaskView.setAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.dd_mask_out));
            if (dropDownListener != null) {
                dropDownListener.onCollapseDropDown();
            }
            isExpanded = false;
        }
    }

    /**
     *  初始化自定义View
     * */
    private void init(Context context, AttributeSet attrs) {
        handleAttrs(context, attrs);
        // 注意这里是 this( 父布局是当前控件---- 一定要记得)----
        inflate(getContext(), R.layout.view_ddv_drop_down, this);
        bindViews();
        setupViews();
    }

    /**
     *  处理自定义的属性
     * */
    private void handleAttrs(Context context, AttributeSet attrs) {
        if (context != null && attrs != null) {
            TypedArray a = context.getTheme().obtainStyledAttributes(
                    attrs,
                    R.styleable.DropDownView,
                    0, 0);

            try {
                backgroundColor = a.getColor(R.styleable.DropDownView_containerBackgroundColor, ContextCompat.getColor(context, R.color.dDVColorPrimary));
                overlayColor = a.getColor(R.styleable.DropDownView_overlayColor, ContextCompat.getColor(context, R.color.dDVTransparentGray));
            } finally {
                a.recycle(); // 进行回收
            }
        }
        // 设置默认的颜色 ---
        if (backgroundColor == 0) {
            backgroundColor = ContextCompat.getColor(context, R.color.dDVColorPrimary);
        }
        if (overlayColor == 0) {
            overlayColor = ContextCompat.getColor(context, R.color.dDVTransparentGray);
        }
    }

    private void setupViews() {
        dropDownHeaderContainer.setOnClickListener(dropDownHeaderClickListener);
        // 头容器
        dropDownHeaderContainer.setBackgroundColor(backgroundColor);

    }

    private void bindViews() {
        dropDownContainer = (LinearLayout) findViewById(R.id.drop_down_container);
        dropDownHeaderContainer = (ViewGroup) findViewById(R.id.drop_down_header);
        mFmContainer = (FrameLayout) findViewById(R.id.fmContainer);
    }

    
    private final OnClickListener dropDownHeaderClickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            if (isExpanded) {
                collapseDropDown();
            } else {
                expandDropDown();
            }
        }
    };

    private final OnClickListener emptyDropDownSpaceClickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            collapseDropDown();
        }
    };
    
    /**
     *  设置监听回调
     * */
    public interface DropDownListener {
        /**
         * 扩张成功
         * This method will only be triggered when {@link #expandDropDown()} is called successfully.
         */
        void onExpandDropDown();

        /**
         * 折叠成功
         * This method will only be triggered when {@link #collapseDropDown()} is called successfully.
         */
        void onCollapseDropDown();
    }
}
<?xml version="1.0" encoding="utf-8"?>
<!--
  <merge /> 标签来减少视图层级结构
  在Android layout文件中需要一个顶级容器来容纳其他的组件,而不能直接放置多个组件
            目的是通过删减多余或者额外的层级,从而优化整个Android Layout的结构。
            直接作用在父布局之下  也就是 RelativeLayout
            如下面的 tools:parentTag = "android.widget.RelativeLayout" 就是很好的解释
  -->

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:parentTag="android.widget.RelativeLayout">

    <!-- 空背景 -->
    <!-- 内容区域-->
    <LinearLayout
        android:id="@+id/drop_down_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true"
        android:orientation="vertical">
        <!-- 控件的头 -->
        <RelativeLayout
            android:id="@+id/drop_down_header"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/dDVColorPrimary"
            android:gravity="center">

            <!-- Will have one child for Header/Collapsed View -->

        </RelativeLayout>
        <FrameLayout
            android:id="@+id/fmContainer"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        </FrameLayout>

        <!-- 这里add的布局必将置于头之下 -->
        <!-- Will have one child after drop_down_header for Expanded View -->

    </LinearLayout>

</merge>
<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.anthonyfdev.dropdownview.DropDownView
        android:id="@+id/drop_down_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:containerBackgroundColor="@color/dropDownContainerColor"
        app:overlayColor="@color/ovlyColor"/>
    <TextView
        android:text="我在底部"
        android:layout_alignParentBottom="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setupViews();
        setupList();

        View view = LayoutInflater.from(this).inflate(R.layout.layout_content, null);
        dropDownView.setHeaderView(collapsedView);
        dropDownView.setExpandedView(expandedView,view);
        dropDownView.setDropDownListener(dropDownListener);
    }
好了,就是这么多,修改的后面也用于我的项目了,读大牛的代码真的学到很多东西,我也渐渐的改变了我之前比较丑陋的代码。



猜你喜欢

转载自blog.csdn.net/crazyzhangxl/article/details/81051988