Android-手势控制的飞掠横幅

叶经略
2023-12-01

1. 布局设置

  • activity_banner.xml,定义BannerFlipper(横幅翻转器,在工具类中编写好,看如下BannerFlipper的编写)
<?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:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <com.example.app12.widget.BannerFlipper
        android:id="@+id/bf_banner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        ></com.example.app12.widget.BannerFlipper>

    <TextView
        android:id="@+id/tv_banner"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=" 点击图片 "
        android:layout_gravity="center"
        android:textSize="17dp"
        android:textColor="@color/black"/>

</LinearLayout>
</LinearLayout>
  • banner_flipper.xml ,定义ViewFlipper和RadioGroup,用于在BannerFlipper工具中定义横幅图片和圆点

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <ViewFlipper
            android:id="@+id/vf_banner"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:flipInterval="3000"
            android:layerType="software"></ViewFlipper>
        <RadioGroup
            android:id="@+id/rg_banner"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingBottom="2dp"
            android:orientation="horizontal"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"></RadioGroup>
    </RelativeLayout>
    
  • drawable中的 indicator_selector.xml ,定义圆点,其中icon_point_n和icon_point_c为灰色圆点和红色圆点

    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_checked="false" android:drawable="@drawable/icon_point_n" />
        <item android:state_checked="true" android:drawable="@drawable/icon_point_c" />
    </selector>
    
  • anim包中定义了灰度动画和平移动画

    push_left_in.xml,描述向左翻页时右边页面的移入行为

    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate android:duration="1500" android:fromXDelta="100.0%p" android:toXDelta="0.0" />
        <alpha android:duration="1500" android:fromAlpha="0.1" android:toAlpha="1.0" />
    </set>
    

    push_left_out.xml ,描述向左翻页时左边页面的移出行为

    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate android:duration="1500" android:fromXDelta="0.0" android:toXDelta="-100.0%p" />
        <alpha android:duration="1500" android:fromAlpha="1.0" android:toAlpha="0.1" />
    </set>
    

    类推,向右翻页时,就是fromXDelta和toXDelta变为相反数

2. BannerFlipper横幅翻转器

package com.example.app12.widget;

import android.app.Activity;
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.LayoutParams;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RelativeLayout;
import android.widget.ViewFlipper;

import com.example.app12.R;
import com.example.app12.util.Utils;

import java.util.ArrayList;

/**
 * Created by Will.Shen on 2021/8/25.
 */

public class BannerFlipper extends RelativeLayout {

    private String TAG = "BAnnerFlipper";
    private Context mContext;
    private ViewFlipper vf_banner2;
    private RadioGroup rg_binner;
    private int dip_15;
    private GestureDetector mGesture;//手势检测器对象
    private float mFlipGap = 20f;//触发飞掠事件的距离阈值
    private LayoutInflater mInflater;
    private int mcount = 0;

    public BannerFlipper(Context context, AttributeSet attrs) {
        super(context,attrs);
        mContext = context;
        initView();
    }

    //初始化试图
    private void initView() {
        Log.e(TAG,"----------initView-----------");
        mInflater = ((Activity)mContext).getLayoutInflater();
        View view = mInflater.inflate(R.layout.banner_flipper,null);
        vf_banner2 = (ViewFlipper)view.findViewById(R.id.vf_banner);
        rg_binner = (RadioGroup)view.findViewById(R.id.rg_banner);
        addView(view);
        dip_15 = Utils.dip2px(mContext,15);
        mGesture = new GestureDetector(mContext,new BannerGestureListener());
    }

    public boolean dispatchTouchEvent(MotionEvent event) {
        mGesture.onTouchEvent(event);
        return true;
    }

    //定义手势检测监听器
    final class BannerGestureListener implements GestureDetector.OnGestureListener {

        @Override
        public boolean onDown(MotionEvent e) {
            return true;
        }
        @Override
        public void onShowPress(MotionEvent e) {
        }
        //在点击时触发
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
           int position = vf_banner2.getDisplayedChild();
            mListener.onBannerClick(position);
            return true;
        }
        //在手势滑动的过程中触发
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            return false;
        }
        @Override
        public void onLongPress(MotionEvent e) {
        }
        //手势飞快掠过时触发
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            if (e1.getX() - e2.getX() > mFlipGap) {//从右往左掠过
                startFlip();
                return true;
            }
            if (e1.getX() - e2.getX() < -mFlipGap) {//从左往右
                backFlip();
                return true;
            }
            return false;
        }
    }

    private void startFlip() {
        Log.e(TAG,"----------startFlip-----------");
        vf_banner2.startFlipping();//开始轮播
        vf_banner2.setInAnimation(AnimationUtils.loadAnimation(mContext, R.anim.push_left_in));
        vf_banner2.setOutAnimation(AnimationUtils.loadAnimation(mContext, R.anim.push_left_out));
        vf_banner2.getOutAnimation().setAnimationListener(new BannerAnimationListener(this));
        vf_banner2.showNext();
    }
    private void backFlip() {
        vf_banner2.startFlipping();
        vf_banner2.setInAnimation(AnimationUtils.loadAnimation(mContext, R.anim.push_right_in));
        vf_banner2.setOutAnimation(AnimationUtils.loadAnimation(mContext, R.anim.push_right_out));
        vf_banner2.getOutAnimation().setAnimationListener(new BannerAnimationListener(this));
        vf_banner2.showPrevious();
        vf_banner2.setInAnimation(AnimationUtils.loadAnimation(mContext, R.anim.push_left_in));
        vf_banner2.setOutAnimation(AnimationUtils.loadAnimation(mContext, R.anim.push_left_out));
        vf_banner2.getOutAnimation().setAnimationListener(new BannerAnimationListener(this));
    }

    //设置飞掠试图的队列
    public void setImage(ArrayList<Integer> imageList) {
        Log.e(TAG,"----------setImage-----------");
        for (int i = 0;i < imageList.size();i++) {
            Log.e(TAG,"----------imageList-----------");
            Integer imageID = ((Integer)imageList.get(i).intValue());
            ImageView iv_item = new ImageView(mContext);
            iv_item.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                            LayoutParams.MATCH_PARENT));
            iv_item.setScaleType(ImageView.ScaleType.FIT_XY);
            iv_item.setImageResource(imageID);
            vf_banner2.addView(iv_item);
        }
        mcount = imageList.size();
        for (int i = 0;i < imageList.size();i++) {
            RadioButton radio = new RadioButton(mContext);
            radio.setLayoutParams(new RadioGroup.LayoutParams(dip_15,dip_15));
            radio.setGravity(Gravity.CENTER);
            radio.setButtonDrawable(R.drawable.indicator_selector);
            rg_binner.addView(radio);
        }
        vf_banner2.setDisplayedChild(imageList.size()-1);
        startFlip();
    }

    private BannerClickListener mListener;//声明横幅点击的监听器对象

    public void setOnBannerListener(BannerClickListener listener) {
        mListener = listener;
    }

    public interface BannerClickListener{//横幅点击的监听器
        void onBannerClick(int position);
    }

    private class BannerAnimationListener implements Animation.AnimationListener { //设置圆点的监听器

        private BannerAnimationListener(BannerFlipper bf) {
        }
        @Override
        public void onAnimationStart(Animation animation) {

        }

        @Override
        public void onAnimationEnd(Animation animation) {//设置圆点颜色变化
            int position = vf_banner2.getDisplayedChild();
            ((RadioButton)rg_binner.getChildAt(position)).setChecked(true);
        }
        @Override
        public void onAnimationRepeat(Animation animation) {

        }
    }
}

3.Activity类

package com.example.app12;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.example.app12.util.DisplayUtil;
import com.example.app12.widget.BannerFlipper;

import java.util.ArrayList;

/**
 * Created by Will.Shen on 2021/8/25.
 */

public class BannerActivity extends AppCompatActivity implements BannerFlipper.BannerClickListener {

    private BannerFlipper bf_banner;
    private TextView tv_banner;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_banner);
        bf_banner = (BannerFlipper) findViewById(R.id.bf_banner);
        tv_banner = (TextView) findViewById(R.id.tv_banner);

        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) bf_banner.getLayoutParams();
        params.height = (int) (DisplayUtil.getSreenWidth(this) * 250f/ 640f);
        bf_banner.setLayoutParams(params);

        ArrayList<Integer> imageArray = new ArrayList<>();
        imageArray.add(Integer.valueOf(R.drawable.guide_bg1));
        imageArray.add(Integer.valueOf(R.drawable.guide_bg2));
        imageArray.add(Integer.valueOf(R.drawable.guide_bg3));
        bf_banner.setImage(imageArray);
        bf_banner.setOnBannerListener(this);
    }

    @Override
    public void onBannerClick(int position) {
        String desc = String.format("你点击了第%d张图",position);
        tv_banner.setText(desc);
    }
}

4. BannerFlipper横幅翻转器所用到的一些工具

Utils

package com.example.app12.util;

import android.annotation.SuppressLint;
import android.content.Context;
import android.view.Menu;
import android.view.ViewConfiguration;
import android.view.Window;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by ouyangshen on 2016/9/11.
 */
public class Utils {
    //根据手机的分辨率从 dp 的单位 转成为 px(像素)
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    //根据手机的分辨率从 px(像素) 的单位 转成为 dp
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

   @SuppressLint("SimpleDateFormat")
   public static String getNowDateTime(String format) {
      SimpleDateFormat sdf = new SimpleDateFormat(format);
      return sdf.format(new Date());
   }

   //如果设备有物理菜单按键,需要将其屏蔽才能显示OverflowMenu
   //API18以下需要该函数在右上角强制显示选项菜单
   public static void forceShowOverflowMenu(Context context) {
      try {
         ViewConfiguration config = ViewConfiguration.get(context);
         Field menuKeyField = ViewConfiguration.class.
               getDeclaredField("sHasPermanentMenuKey");
         if (menuKeyField != null) {
            menuKeyField.setAccessible(true);
            menuKeyField.setBoolean(config, false);
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
   }

   // 显示OverflowMenu的Icon
   public static void setOverflowIconVisible(int featureId, Menu menu) {
      // ActionBar的featureId是8,Toolbar的featureId是108
      if (featureId % 100 == Window.FEATURE_ACTION_BAR && menu != null) {
         if (menu.getClass().getSimpleName().equals("MenuBuilder")) {
            try {
               Method m = menu.getClass().getDeclaredMethod(
                     "setOptionalIconsVisible", Boolean.TYPE);
               m.setAccessible(true);
               m.invoke(menu, true);
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
      }
   }

}

DisplayUtils

package com.example.app12.util;

import android.content.Context;
import android.util.DisplayMetrics;
import android.view.WindowManager;

/**
 * Created by ouyangshen on 2016/9/11.
 */
public class DisplayUtil {

    public static int getSreenWidth(Context ctx) {
        WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(dm);
        return dm.widthPixels;
    }

    public static int getSreenHeight(Context ctx) {
        WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(dm);
        return dm.heightPixels;
    }

    public static float getSreenDensity(Context ctx) {
        WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(dm);
        return dm.density;
    }

}
 类似资料: