一、 需求介绍

因为事务模块的扩张,导致各类事务进口张狂增加,原先的进口UI款式现已满意不了产品经理的需求了,所以他们提出了能不能仿照淘宝的事务导航栏给咱们自己的产品做一个优化,废话不手机银行多说,原先他们的需求大致是这样的↓

仿Android淘宝顶部导航栏的HorizontalScrollView Demo实例(源码在文末)

终究经过产品和老板们的商讨,终究共同决议选用如下款式产品设计

仿Android淘宝顶部导航栏的HorizontalScrollView Demo实例(源码在文末)

能够看到,这个和淘宝的区别在于:

  1. 咱们默许显现 4 个图标,而且有左右两个“箭头”,当ScrollView不能继续向左滑动时,左箭头躲藏,反之不能继续向右滑动时,右箭头躲藏。其他状况 “箭头”都显现。
  2. 用户能够选择点击“箭头”来完结滑动,也能够用手指左右拖动来进行滑动。当用户经过点击“appointment箭头”滑动时,点击一次滑动4个图标,不能有偏差
  3. 咱们只需一行图标需求滑动,产品经理怎么入行而淘宝是两行。
  4. 此外咱们还有个额定需求产品设计,当图标露出超越五分之四时,则把此图标视手机搬家为可见状况,此刻当用户点击”箭头“滑动时,就需求越过此图标(也便是直接向后滑动4个)。反approve之图标为不行见状况时,用户点击”箭头“滑动时,则不能越过此图标(也便是向后滑动3个图标)。例如:源码时代下图的 游乐园 图标,此刻它被 ”左箭头“给遮挡了,可是超越五分之四的部分显现出来了手机号最旺财的尾数,则视为可见状况,当用户点击 ”左箭头“ 进行滑动时,则越过此图标,直接向后滑动4个图标。而且经过 ”箭头“ 滑动后产品策略的图标不能处于被遮挡状况,都需求完整的进行显现。

仿Android淘宝顶部导航栏的HorizontalScrollView Demo实例(源码在文末)

看到了手机搬家大致需求后,咱们来做一下技术剖析,首先映入眼帘的是一个可左右翻滚的View,这个第一时间想到能够用 HorizontalScrollView 来完结,但细心一看底部有一个小进展条,这个在 Horizappearanceon源码talScrollView 是没有现approach成的能够运用的,所以只能自己去自界说一个了。终究还有产品运营便是完结左右“箭头”的点击滑动。手机

仿Android淘宝顶部导航栏的HorizontalScrollView Demo实例(源码在文末)

所以,咱们这个需求需产品经理大赛有哪些求拆分成以下几步来完结:

  1. 引进HorizontalScrollView,把图标塞进这个HorizontalScrollView中进行展产品定位appearance
  2. 自界说底部进展条源码编辑器(BottomProgressBar)
  3. 参加可点击源码交易平台的左右“箭手机搬家头”,并完结点击后主动滑动4个图标的功用

二、 代码完结

本 Demo 的代码我会放在GitHub上,有需求的看官能够apple自行去Clone,但请别忘了点赞哦。

1. 引进 HorizontalScr产品经理的职责ollView

在布局文件 activity_main.xml 中,咱们引进HorizonappeartalSAPPcrollView, 代码如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".MainActivity">
    <!-- 可翻滚的进口 -->
    <HorizontalScrollView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:id="@+id/horizontalScrollView"
        android:scrollbars="none"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent">
        <include
            layout="@layout/entry_layout"
            android:id="@+id/entry_layout" />
    </HorizontalScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

其间需求留意 include 标签,其作用是能够将一个指定布局文件加载到当时布局文件中,其间的 entry_layout.xml布局文件里放了源码网站13个图源码编辑器下载标,运用include标签能够使得主布局文件看起来不会显得很冗长,便利直观的看出布局结构。

源码时代局文件增加好后,就需产品经理是不是销售求把这13个进口图标全都塞进去,而且在MainActivity里引进布局,然后就会得到这样的一个作用

仿Android淘宝顶部导航栏的HorizontalScrollView Demo实例(源码在文末)

2. 自界说底部进展条(BottomProgressBar)

这个进展条布局非常简略,就一个白色底板和一个蓝色进展,所以咱们先给他界说一个布局如下:

<?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="wrap_content"
    android:background="@drawable/entry_progress_bar_background"
    android:orientation="horizontal">
    <View
        android:layout_width="20dp"
        android:layout_height="match_parent"
        android:id="@+id/progress_view"
        android:background="@drawable/entry_progress"/>
</LinearLayout>

appetite间的android:background特点里的 @drawable/entry_progress_bar_background@drawable/entry_progress都是经过圆角处理后的矩形,这里只放一个代码了,有需求的能够直接去GitHub上自取

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item >
        <shape android:shape="rectangle">
            <solid android:color="#2182FC"/>
            <corners android:radius="50dp"/>
        </shape>
    </item>
</layer-list>

然后在EntryProgressBar.java中引进此布局,并界说一个能够更新进展的办法setProgressappreciate()apple体代码如下:

package com.example.entry_demo.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.TranslateAnimation;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import com.example.entry_demo.R;
import com.example.entry_demo.util.ViewHelper;
/**
 * 次进口的蓝色小翻滚条
 */
public class EntryProgressBar extends LinearLayout {
    View progress_view;
    private float currentX = 0;
    //每一页展现的icon个数
    private float display_count = 4;
    //icon的总个数
    private float total_count = 5;
    //进展条组件总宽度32dp
    private float width_dp = 32;
    //蓝色进展的宽度dp
    private float blue_width_dp = 20;
    public EntryProgressBar(Context context) {
        this(context, null);
    }
    public EntryProgressBar(Context context,@Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }
    private void init(Context context) {
        // 引进布局
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.entry_progress_bar_layout, this);
        progress_view = this.findViewById(R.id.progress_view);
        setBlueWidth();
    }
    //设置蓝色进展的width
    private void setBlueWidth() {
        blue_width_dp = (display_count / total_count) * width_dp;
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                ViewHelper.dp2px(getContext(),blue_width_dp), LayoutParams.MATCH_PARENT);
        progress_view.setLayoutParams(params);
    }
    // 设置总的进口个数
    public void setTotalCount(int total_count) {
        this.total_count = total_count;
        setBlueWidth();
    }
    // 更新进展
    public void setProgress(float percent_progress) {
        float progress = 0;
        progress = ViewHelper.dp2px(getContext(), width_dp - blue_width_dp) * percent_progress;
        // 开启动画
        TranslateAnimation animation = new TranslateAnimation(currentX, progress, 0, 0);
        animation.setDuration(200);
        // 设置动画往后不复位
        animation.setFillEnabled(true);
        animation.setFillAfter(true);
        progress_view.startAnimation(animation);
        currentX = progress;
    }
}

手机银行究在 MianActivity 的onCreate()办法中给HorizontalView增加一个翻滚监听,而且在监听中时刻更新 EntryProgressBar的进展,代码如下:

// 给HorizontalScrollView 增加滑动监听
private void addListenerToHorizontalView() {
    // 运用post办法是因为,在构建进口时,需求先获取View的宽度,可是只需当View进行到onLayout阶段后,才能得到准群的width,所以才调用view.post办法来确保获取到width是精确的
    horizontalScrollView.post(new Runnable() {
        @Override
        public void run() {
            horizontalScrollView_width_dp = ViewHelper.px2dip(context, horizontalScrollView.getWidth());
        }
    });
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        horizontalScrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
            @Override
            public void onScrollChange(View view, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
                float left_btn_alpha = 1.0f;
                float right_btn_alpha = 1.0f;
                // 获取可滑动范围
                int scrollRange = 0;
                int scroll_X = horizontalScrollView.getScrollX();
                if (horizontalScrollView.getChildCount() > 0) {
                    View child = horizontalScrollView.getChildAt(0);
                    scrollRange = Math.max(0, child.getWidth() - (horizontalScrollView.getWidth() - (horizontalScrollView.getPaddingLeft() + horizontalScrollView.getPaddingRight())));
                }
                if (scrollRange > 0 ) {
                    // 同步更新底部蓝色进展条
                    entry_progress_bar.setProgress((float) scroll_X / scrollRange);
                    //设置左右按钮的突变透明度
                    if (scroll_X >= scrollRange - ViewHelper.dp2px(context, 40)) {
                        left_btn_alpha = (float) (scrollRange - scroll_X) / ViewHelper.dp2px(context, 40);
                    } else if (scroll_X < ViewHelper.dp2px(context, 40)) {
                        right_btn_alpha = (float) scroll_X / ViewHelper.dp2px(context, 40);
                    }
                }
            }
        });
    }
}

到此为止,咱们得到了这样的一个作用,能够看产品经理到进展条现已制造完结了:

仿Android淘宝顶部导航栏的HorizontalScrollView Demo实例(源码在文末)

3. 参加可点击的左右“箭头”,并完结点击滑动源码1688功用

因为老板们给的需求是,默许只显现4个图标,但现在显现了5个,所以在完结”箭头“点击功用之前,咱们还需求修改下每个图标之间的距离,确保在不同分辨率下,都只显现出4个图标,而且此产品定位刻蓝色进展条的翻滚不是很明显,咱们也需求调用setTotalCount()来让翻滚变得更加简appstore单区分,具体代码如下:

    private void initEntry() {
        List<EntryView> entriesTemp = Arrays.asList(abs_1, cbs_2, ctbs_3, destination_4, gbs_5, home_fbs_6, lbs_7, nlbs_8, pbs_9, rbs_10, staycation_11, tbs_12, frbs_13);
        // 依据屏幕巨细核算icon之间的距离
        int entrySize = entriesTemp.size();
        if (entrySize > 4) {
            // 之所以减去64dp,是因为右滑按钮占40dp,marginStar占24dp
            entry_margin_dp = (horizontalScrollView_width_dp - icon_width_dp * 4 - 64) / 4;
            // 设置图标总数
            entry_progress_bar.setTotalCount(entrySize);
        } else {
            // 之所以减去48dp,是因为当进口小于4个时,左右按钮躲藏,但marginStar和marginEnd各占24dp
            entry_margin_dp = (horizontalScrollView_width_dp - icon_width_dp * 4 -48) / 3;
            entry_progress_bar.setVisibility(View.GONE);
        }
        for (int i = 0; i < entriesTemp.size(); i ++) {
            EntryView entry = entriesTemp.get(i);
            int margin = entry_margin_dp;
            if (i < entrySize) {
                if (i == entrySize - 1) {
                    //设置终究一个进口的margin
                    margin = 24;
                }
                ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) entry.getLayoutParams();
                if (i == 0) {
                    params.setMarginStart(ViewHelper.dp2px(context, 24));
                }
                params.setMarginEnd(ViewHelper.dp2px(context, margin));
                entry.setLayoutParams(params);
            } else {
                entry.setVisibility(View.GONE);
            }
        }
    }

然后增加左右”箭头“比较简略,直接在activity_appetitemain.xml中增加既可,可是难点在于怎么经过点击它,稳定的进行滑动,而且满意额定appstore需求

此外咱们还有个额定appreciate需求,当图标露出超越五分之四时,则把此图标视为可见状况,此刻当用户点击”箭头“滑动时,就需求越过此图标(也便是直接向后滑动4个)。反之图标为不行见状况时,用户点击”箭头“滑动时,则不能越过此图标(也便是向后手机克隆滑动3个图标)。例如:下图的 游乐园 图标,此刻它被 ”左箭头“给遮挡了,可是超越五分之四的部分显现出来了,则视为可见状况,当用户点击 ”左箭头“ 进行滑动时,则越过源码精灵永久兑换码此图标,直APP接向后滑动4个图标。而且经过 ”箭头“ 滑动后的图标不能处于被遮挡状况,都需求完整的进行显现。

这个问题的处理其实就相当于一个小算法实践了,我的处理是用一个List产品生命周期去记录下每个图标滑动了多少dp后到达可见与不行见时的分界线,例如:对于第一个图标而言,因为图标巨细是60dp,当我向左滑动60d手机管家p后,它就被彻底遮挡了,所以这个60便是图标1的索引坐标,当然其间还有许多细节,有需求的看官能够看如下代码:


//设置索引坐标,用于判别次进口是否处于显现状况
private List<Integer> visible_index = new ArrayList<Integer>();
...
...
...
// 初始化索引坐标
int index = 0;
visible_index.add(0, 0);
for (int i = 1; i <= entrySize - 4; i++) {
    // 留意,entrySize - 4 是因为默许每一页显现4个图标,当HorizontalView不行滑动时,这4个图标一定是处于可见状况的,所以不必参加到索引坐标中
    // 第一个和终究一个可见索引坐标为60dp,其他均为60dp + secondaryEntry_margin_dp
    if (i == 1 || i == entrySize - 4) {
        index += icon_width_dp;
    } else {
        index += (icon_width_dp + entry_margin_dp);
    }
    visible_index.add(i, index);
}

终究便是点击”箭头“今手机号最旺财的尾数后滑动4个的功用,因为咱们图标巨细固产品运营定(IconWidth = 60dp),图标与图标之间的间隔(entry_margin_dp)也是固定的,所以咱们只需向左或许向右滑动(IconWidth + e产品经理ntry_margin_dp) * 4 便appear是一次滑动4个的手机搬家功用了,代码我就不在这贴了,仍是那句话,有需求的看官能够直接去看下源码。

终究的终究,咱们得到了这样完结了这个需求。

人为手动左右滑动作用如下:

仿Android淘宝顶部导航栏的HorizontalScrollView Demo实例(源码在文末)



用户点击”箭头源码之家“后的滑动作用如下:

仿Android淘宝顶部导航栏的HorizontalScrollView Demo实例(源码在文末)

总结手机耗电快怎么办

之所以把这个需求拿出来做个demo共享,是因为里边触及许多常见面试内容和基础,例如:自界说View的运用、常用布局和布局优化、不同手机分辨率下怎么确保布局的共同性等。

APP码里也有许多appointment细节我经过注释的形式记录了下来,在文章中就没有一一列举了,例如 我在以往的文章 谈一谈你对View的认识和View的工作流程 一文中具体介绍了View三大流程,其间就介绍到为何要运用post,假如这里不运用post办法,那么将会appearance获取不到正确的宽度,导致UI显现达不到预期作用,手动能力强的看官能够自行实践,这也是不断前进的确保。

// 运用post办法是因为,在构建进口时,需求先获取View的宽度,可是只需当View进行到onLayout阶段后,才能得到准群的width,所以才调用view.post办法来确保获取到width是精确的
horizontalScrollView.post(new Runnable() {
    @Override
    public void run() {
        horizontalScrollView_width_dp = ViewHelper.px2dip(context, horizontalScrollView.getWidth());
        initEntry();
    }
});

终究假如你在某一方面发现了bug或许有更好的办法能够appreciate替代,欢迎和我进行共享,一起前进,终究还愣着干嘛,点个赞吧! 附上源码