天天看點

彈出浮層基類 BaseFloating 封裝

之前在一個技術群裡面有位同學請教大家這種彈出窗怎麼做(如下圖所示),他是用的PopWindow實作的,但是PopWindow有很多地方不符合他的要求,比如彈出的起始位置,彈出的動畫效果。

彈出浮層基類 BaseFloating 封裝

我們項目中也會有這種彈出浮層的需求,舉個例子,下面是仿淘寶選擇規格的彈出浮層效果,不僅僅是這一處,整個項目中也會有很多類似的彈出浮層效果,為了友善,我們就需要有一個彈出浮層基類,來簡化我們的代碼。下面我就來介紹下我們項目的解決方案。

彈出浮層基類 BaseFloating 封裝

我們的實作思路是:将彈出浮層的View 獨立封裝,然後擷取到這個View的執行個體,add 到我們需要顯示的螢幕 decodeView 上,是以我們預設那個需要使用的頁面 最外層使用的是 FramLayout (具體原因大家可以百度 FramLayout ,了解下它的屬性),在add到螢幕上後,将 浮層View隐藏,在觸發條件後再給View 配上相應的彈出動畫顯示。下面我們看下這個基類:

package com.example.jin.floating.view;

import android.animation.Animator;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

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

/**
 * 浮層基類
 * 
 */
public abstract class BaseFloatinglayer {

    protected Context mContext;
    protected  int resourceId;//布局頁面id


    protected ViewGroup content, resource_View;

    protected FrameLayout decorView;

    protected LinearLayout background;

    protected Intent intentData;

    protected boolean click_background_close=true;

    protected  boolean isInited=false;

    protected  FloatingCallBack mFloatingCallBack;

    protected boolean isAnimating=false;//動畫正在執行
    protected   boolean isShowed=false;//頁面已經顯示

    protected List<Animator> mAnimatorList = new ArrayList<Animator>();


    public BaseFloatinglayer(Context ctx, int ResourceId){
        this.mContext=ctx;
        this.resourceId =ResourceId;
        InitView();

    }


    public abstract void openShow();//打開顯示動畫
    public abstract  void closeHide();//關閉隐藏
    public abstract  void findView(ViewGroup resource_View);//頁面初始化成功傳回布局Resource_View
    public abstract  void hide();//初始化時隐藏
    public abstract  void setIntentData(Intent intentData);//視圖打開後擷取資料

    public void setmFloatingCallBack(FloatingCallBack mFloatingCallBack) {
        this.mFloatingCallBack = mFloatingCallBack;
    }

    public boolean isShowed() {
        return isShowed;
    }

    public View getView() {
        return this.resource_View;
    }


    protected void InitView(){
        LayoutInflater inflater = LayoutInflater.from(mContext);
        decorView = (FrameLayout) ((ViewGroup)((Activity)mContext).getWindow().getDecorView().findViewById(android.R.id.content));//.getChildAt(0)
        content = (ViewGroup) decorView.getChildAt(0);
        resource_View = (ViewGroup) inflater.inflate(resourceId, null);

        InitBackground();
        findView(resource_View);
        isInited=true;
    }


    /**
     * 初始化背景層
     */
    protected void InitBackground(){
        background=new LinearLayout(mContext);
        background.setBackgroundColor(Color.BLACK);
        background.setOrientation(LinearLayout.VERTICAL);
        background.setVisibility(View.GONE);
        background.setAlpha(0);
        background.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
        background.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (click_background_close) {
                    closeHide();
                }
            }
        });
    }
    public View Background(){
        return  this.background;
    }

    /**
     * 添加到頂層
     */
    public void ViewAdd(){

        if(decorView.findViewById(resourceId)==null){//如果目前頁面沒有則addView
            decorView.addView(background);
            decorView.addView(resource_View);
        }
    }

    /**
     * 添加到頂層
     * @param VL
     */
    public void ViewAdd(ViewGroup.LayoutParams VL){

        if(decorView.findViewById(resourceId)==null){//如果目前頁面沒有則addView
            decorView.addView(background);
            decorView.addView(resource_View,VL);
        }
    }



    public void ViewAddMoBackgrond(ViewGroup.LayoutParams VL){

        if(decorView.findViewById(resourceId)==null){//如果目前頁面沒有則addView
            decorView.addView(resource_View,VL);
        }
    }

    /**
     * 添加到頂層
     * @param childid
     */
    public void ViewAddToParent(int childid){

        if(content.findViewById(resourceId)==null){//如果目前頁面沒有則addView
            content.addView(background);
            content.addView(resource_View);
        }
    }

    /**
     * 添加到指定索引
     * @param childid
     * @param VL
     */
    public void ViewAddToParent(int childid, ViewGroup.LayoutParams VL){

        if(content.findViewById(resourceId)==null){//如果目前頁面沒有則addView
            content.addView(background, childid, VL);
            content.addView(resource_View, childid+1,VL);
        }
    }

}           

我們來看下主要實作代碼部分:

1、擷取到螢幕,以及傳入的 浮層View 的 布局xml 檔案id,其中resourceId就是傳入的xml 檔案id:

LayoutInflater inflater = LayoutInflater.from(mContext);
decorView = (FrameLayout) ((ViewGroup)((Activity)mContext).getWindow().getDecorView().findViewById(android.R.id.content));//.getChildAt(0)
content = (ViewGroup) decorView.getChildAt(0);
resource_View = (ViewGroup) inflater.inflate(resourceId, null);           

2、将執行個體化擷取到的浮層View add 到螢幕上,這裡我們封裝了幾個add方法,分别是預設添加到頂層; 添加到置頂索引位置; 同時添加陰影浮層(這個陰影浮層是夾在螢幕和浮層View之間的);根據傳入的 LayoutParams 添加浮層,可以滿足多種需求,如果有新的需求,也可以在其中添加相應方法。

public void ViewAdd(){
    if(decorView.findViewById(resourceId)==null){//如果目前頁面沒有則addView
        decorView.addView(background);
        decorView.addView(resource_View);
    }
}

/**
 * 添加到頂層
 * @param childid
 */
public void ViewAddToParent(int childid){

    if(content.findViewById(resourceId)==null){//如果目前頁面沒有則addView
        content.addView(background);
        content.addView(resource_View);
    }
}

/**
 * 添加到頂層
 * @param VL
 */
public void ViewAdd(ViewGroup.LayoutParams VL){

    if(decorView.findViewById(resourceId)==null){//如果目前頁面沒有則addView
        decorView.addView(background);
        decorView.addView(resource_View,VL);
    }
}           

3、 這裡還封裝了幾個abstract 方法,對應彈出,隐藏,擷取控件,接收傳入資料等操作,以實作需要在浮層View進行一些進一步的操作:

public abstract void openShow();//打開顯示動畫
public abstract  void closeHide();//關閉隐藏
public abstract  void findView(ViewGroup resource_View);//頁面初始化成功傳回布局Resource_View
public abstract  void hide();//初始化時隐藏
public abstract  void setIntentData(Intent intentData);//視圖打開後擷取資料           

4、 同時我們還封裝了個回調接口,可以讓我們接收到開始彈出,結束彈出,開始隐藏,結束隐藏的動作:

public interface FloatingCallBack {
    void OpenStart();
    void OpenEnd();
    void CloseStart();
    void CloseEnd();
}           

介紹了原理,我們來介紹下使用方法:建立類檔案,繼承基類,在 openShow 和 closeHide 方法中寫你需要的彈出,隐藏動畫(我這裡隻是舉例了從下方彈出和收起到下方,也可以替換成上下左右4個方向的,任意都可以,看你的具體需求了),并且在Hide方法中将其影藏:

@Override
public void hide() {
    resource_View.setTranslationY(DensityUtils.getHeight(mContext));
    resource_View.setVisibility(View.GONE);
    isAnimating=false;
    isShowed=false;
}           

我們來看下已經寫好了的 TestFloating:

package com.example.jin.floating.view;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.Intent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;

import com.example.jin.floating.DensityUtils;
import com.example.jin.floating.R;

import java.util.Collections;

/**
 * Created by Jin on 2016/8/11.
 * 用途 :
 */
public class TestFloating extends BaseFloatinglayer {


    private RelativeLayout touch_close;

    public TestFloating(Context ctx, int ResourceId) {
        super(ctx, ResourceId);
    }

    @Override
    public void openShow() {
        if(!isAnimating&&!isShowed){

//            touch_to_close.setBackgroundResource(R.color.transparent);

            isAnimating=true;
            AnimatorSet m_AnimatorSet = new AnimatorSet();
            resource_View.setVisibility(View.VISIBLE);

            ObjectAnimator Resource_View_TRANSLATION_Y= ObjectAnimator.ofFloat(resource_View, View.TRANSLATION_Y, DensityUtils.getHeight(mContext),0 );
            mAnimatorList.clear();
            Collections.addAll(mAnimatorList, Resource_View_TRANSLATION_Y);
            m_AnimatorSet.playTogether(mAnimatorList);
            m_AnimatorSet.setDuration(300);
            m_AnimatorSet.start();
            m_AnimatorSet.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {
                    if(mFloatingCallBack!=null){
                        mFloatingCallBack.OpenStart();
                    }

                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    if(mFloatingCallBack!=null){
                        mFloatingCallBack.OpenEnd();
                    }
                    isAnimating=false;
                    isShowed=true;

//                    touch_to_close.setBackgroundResource(R.color.back_ground_transpant);
                }

                @Override
                public void onAnimationCancel(Animator animation) {
                    if(mFloatingCallBack!=null){
                        mFloatingCallBack.OpenEnd();
                    }
                    isAnimating=false;
                }

                @Override
                public void onAnimationRepeat(Animator animation) {

                }
            });

        }
    }

    @Override
    public void closeHide() {
        if(!isAnimating&&isShowed) {

//            touch_to_close.setBackgroundResource(R.color.transparent);


            isAnimating=true;
            isShowed=false;
            AnimatorSet m_AnimatorSet = new AnimatorSet();
            ObjectAnimator Resource_View_TRANSLATION_Y = ObjectAnimator.ofFloat(resource_View, View.TRANSLATION_Y, 0,DensityUtils.getHeight(mContext));
            mAnimatorList.clear();
            Collections.addAll(mAnimatorList, Resource_View_TRANSLATION_Y);
            m_AnimatorSet.playTogether(mAnimatorList);
            m_AnimatorSet.setDuration(300);
            m_AnimatorSet.start();
            m_AnimatorSet.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {

                    if(mFloatingCallBack!=null){

                        mFloatingCallBack.CloseStart();
                    }
                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    resource_View.setVisibility(View.GONE);
                    isAnimating=false;
                    if(mFloatingCallBack!=null){
                        mFloatingCallBack.CloseEnd();
                    }
                }

                @Override
                public void onAnimationCancel(Animator animation) {

                }

                @Override
                public void onAnimationRepeat(Animator animation) {

                }
            });



        }
    }

    @Override
    public void findView(ViewGroup resource_View) {
        touch_close= (RelativeLayout) resource_View.findViewById(R.id.touch_close);
        touch_close.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                closeHide();
            }
        });
    }

    @Override
    public void hide() {
        resource_View.setTranslationY(DensityUtils.getHeight(mContext));
        resource_View.setVisibility(View.GONE);
        isAnimating=false;
        isShowed=false;
    }

    @Override
    public void setIntentData(Intent intentData) {

    }
}
           

剩下的就是在我們的Activity中調用,先執行個體化:

private void initFloating() {
    testFloating=new TestFloating(this,R.layout.floating_layout);
    FrameLayout.LayoutParams couponsFloatingFL = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    testFloating.ViewAddMoBackgrond(couponsFloatingFL);
    testFloating.hide();
}           

在需要觸發的地方調用 openShow方法就可以了:

testFloating.openShow();           

我們來看一下效果:

彈出浮層基類 BaseFloating 封裝