Android滑动ScrollView时使导航栏停留的效果(仿ios的tableview分区)

本文介绍如何在Android中实现类似iOS的TableView分区效果,当ScrollView滑动时,导航栏(TextView)停留在滚动条目顶部。通过重写ScrollView,监听滑动距离,动态调整导航栏位置,当条目滑出屏幕时,导航栏跟随滚动,营造停留感。文章提供详细布局解析和关键代码实现。


        首先,我们来看下效果图,如下:


 


       让我们来分析一下这个图,这是一个scrollview,有4个条目,当我们向上滑动的时候,如果条目2滑到顶端,会把

条目1顶上去,然后条目2停留,等待条目3上来,之后条目3会把条目2顶上去,然后停留,后面同理。

那么,这个动态的怎么来实现呢?停留的导航栏是什么呢?


        其实,导航栏是一个浮在scrollView上面的一个textView。当条目2滚动到条目1下面的时候,让textview动态的随

着scrollview的滚动而滚动直至条目1滚出屏幕。当条目2滚动到条目1的位置的时候让textView显示在条目1 的位置,

当scrollview继续滚动的时候textview不动,给人一种条目2停留的感觉。以下同理。


        好,大概的原理我们已经大致的清楚了,那就开始来看一下布局吧!


<span style="font-size:18px;"><span style="font-size:18px;"><?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">

    <com.example.peiwc.iostableview.MyScrollView
        android:id="@+id/scv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:id="@+id/title0"
                android:layout_width="match_parent"
                android:layout_height="55dp"
                android:background="#ffF19EDB"
                android:gravity="center"
                android:textStyle="bold"
                android:text="条目1" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:adjustViewBounds="true"
                android:gravity="center"
                android:scaleType="fitCenter"
                android:text="text1"
                android:textSize="26sp" />

            <TextView
                android:id="@+id/title1"
                android:layout_width="match_parent"
                android:layout_height="55dp"
                android:background="@color/colorRed"
                android:gravity="center"
                android:text="条目2"
                android:textStyle="bold"
                android:textSize="22sp"
                android:textColor="@android:color/white"/>


            <TextView
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:adjustViewBounds="true"
                android:gravity="center"
                android:scaleType="fitCenter"
                android:text="text2"
                android:textSize="26sp" />

            <TextView
                android:id="@+id/img"
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:adjustViewBounds="true"
                android:gravity="center"
                android:scaleType="fitCenter"
                android:text="text3"
                android:textSize="26sp" />

            <TextView
                android:id="@+id/title2"
                android:layout_width="match_parent"
                android:layout_height="55dp"
                android:background="@color/colorYel"
                android:gravity="center"
                android:text="条目3"
                android:textSize="22sp"
                android:textColor="@android:color/white"
                android:textStyle="bold"/>


            <TextView
                android:id="@+id/iv1"
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:adjustViewBounds="true"
                android:gravity="center"
                android:scaleType="fitCenter"
                android:text="text4"
                android:textSize="26sp" />

            <TextView
                android:id="@+id/title3"
                android:layout_width="match_parent"
                android:layout_height="55dp"
                android:background="@color/colorBlue"
                android:gravity="center"
                android:text="条目4"
                android:textSize="22sp"
                android:textColor="@android:color/white"
                android:textStyle="bold"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:adjustViewBounds="true"
                android:gravity="center"
                android:scaleType="fitCenter"
                android:text="text5"
                android:textSize="26sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:adjustViewBounds="true"
                android:gravity="center"
                android:scaleType="fitCenter"
                android:text="text6"
                android:textSize="26sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:adjustViewBounds="true"
                android:gravity="center"
                android:scaleType="fitCenter"
                android:text="text7"
                android:textSize="26sp" />
        </LinearLayout>
    </com.example.peiwc.iostableview.MyScrollView>
    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="55dp"
        android:background="@color/colorZi"
        android:gravity="center"
        android:textSize="22sp"
        android:textColor="@android:color/white"
        android:textStyle="bold"
        android:text="条目1" />

</RelativeLayout></span></span>

       这个布局整体是个Relativelayout,里面在scrollview上面有一个textview title,我们给4个条目都设置了id,分别是title0,title1,title2,title3.可以看到scrollview被重写了


       为什么要重写scrollview呢?


       因为我们要对scrollview进行监听,获得scrollview滑动的距离,从而控制title的位移。可惜sdk并没哟响应的方法,但是scrollview中倒是提供了一个方法


<pre name="code" class="java"><span style="font-size:18px;">  @Override
    protected void onScrollChanged(int x, int y, int oldx, int oldy) {
        super.onScrollChanged(x, y, oldx, oldy);
        if (scrollViewListener != null) {
            scrollViewListener.onScrollChanged( x, y, oldx, oldy);
        }
    }</span>



显然这个方法是不能被外界调用的,因此我们要把它暴露出去,方便使用。

         下面帖上程序代码我们来具体看下ScrollView:

<pre name="code" class="java"><span style="font-size:18px;">public class MyScrollView extends ScrollView {
    private ScrollViewListener scrollViewListener = null;


    public MyScrollView(Context context) {
        super(context);
    }

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

    public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    public interface ScrollViewListener {
        void onScrollChanged( int x, int y, int oldx, int oldy);
    }
    public void setScrollViewListener(ScrollViewListener scrollViewListener) {
        this.scrollViewListener = scrollViewListener;
    }
//    onScrollChanged里面有4个参数,l代表滑动后当前ScrollView可视界面的左上角在整个ScrollView的X轴中的位置,oldi也就是滑动前的X轴位置了。
//    同理,t也是当前可视界面的左上角在整个ScrollView的Y轴上的位置,oldt也就是移动前的Y轴位置了。
    @Override
    protected void onScrollChanged(int x, int y, int oldx, int oldy) {
        super.onScrollChanged(x, y, oldx, oldy);
        if (scrollViewListener != null) {
            scrollViewListener.onScrollChanged( x, y, oldx, oldy);
        }
    }

}
</span>




       接下来,就是我们华丽丽的Activity闪亮登场啦!先把整篇帖上去,然后我再仔细讲。

<pre name="code" class="java"><span style="font-size:18px;">public class MainActivity extends Activity implements MyScrollView.ScrollViewListener{
    private TextView title0,title1,title2,title3,title;
    private long topDistance0,topDistance1,topDistance2,topDistance3,height;
    private MyScrollView scrollView;
    private int distance;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        title0=(TextView)findViewById(R.id.title0);
        title1=(TextView)findViewById(R.id.title1);
        title2=(TextView)findViewById(R.id.title2);
        title3=(TextView)findViewById(R.id.title3);
        title=(TextView)findViewById(R.id.title);
        scrollView=(MyScrollView)findViewById(R.id.scv);
        scrollView.setScrollViewListener(this);
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if(hasFocus){
            topDistance0 = title0.getTop();
            topDistance1 = title1.getTop();
            topDistance2 = title2.getTop();
            topDistance3 = title3.getTop();//按钮左上角相对于父view(LinerLayout)的y坐标
            height = title.getMeasuredHeight();
            Log.i("","=topDistance0="+topDistance0);
            Log.i("","=topDistance1="+topDistance1);
            Log.i("","=topDistance2="+topDistance2);
            Log.i("","=topDistance3="+topDistance3);
            Log.i("","=topDistance3="+topDistance3);
            Log.i("","=height="+height);
        }
    }
    @Override
    public void onScrollChanged(int x, int y, int oldx, int oldy) {
        Log.i("","=topDistance=distance="+"x="+x+";y="+y+";oldx="+oldx+";oldy"+oldy);
        distance=y;
        distancePlace(topDistance0,topDistance1);
        distancePlaceLayout(topDistance1,topDistance0);
        distancePlace(topDistance1,topDistance2);
        distancePlaceLayout(topDistance2,topDistance1);
        distancePlace(topDistance2,topDistance3);
        distancePlaceLayout(topDistance3,topDistance2);

        if (distance > topDistance3) {
            title.layout(0,0,title.getRight(),(int)height);
            showText(topDistance3);
        }
    }
    private void distancePlaceLayout(long topDistance1,long topDistance0){
        if(distance>topDistance1-height && distance<topDistance1 ){
            title.layout(0,(int)(topDistance1-distance-height),title.getRight(),(int)(topDistance1-distance));
            showText(topDistance0);
        }
    }
    private void distancePlace(long topDistance0,long topDistance1){
        if(distance >= topDistance0&& distance<topDistance1-height){
            title.layout(0,0,title.getRight(),(int)height);
            showText(topDistance0);
        }
    }
    private void showText(long topDistance){
        if(topDistance==topDistance0){
            title.setText("条目1");
            title.setBackground(getResources().getDrawable(R.color.colorZi));
        }
        if(topDistance==topDistance1){
            title.setText("条目2");
            title.setBackground(getResources().getDrawable(R.color.colorRed));
        }
        if(topDistance==topDistance2){
            title.setText("条目3");
            title.setBackground(getResources().getDrawable(R.color.colorYel));
        }
        if(topDistance==topDistance3){
            title.setText("条目4");
            title.setBackground(getResources().getDrawable(R.color.colorBlue));
        }


    }
}</span>



        在onWindowFocusChanged(boolean hasFocus)中,获取了4个条目的getTop值,即条目到scrollview顶端的距离。注意:getTop是控件左上角相对于父布局的y坐标。这个布局中所有条目的父布局都是scrollview,所以我们这里可以直接使用。我们看下打印的结果:

看下onScrollChange方法:

          当条目2到达条目1低端的时候,滑动的距离就应该是topDistance1-height,即title1.getTop-height,height是条目的高度。那么当条目2还没有到达条目1 低端的时候,如图,我们做了如下的判断:

<pre name="code" class="java"><span style="font-size:18px;">if(distance >= topDistance0 && distance<topDistance1-height){
            title.layout(0,0,title.getRight(),(int)height);
            showText(topDistance0);
        }</span>




          当条目2到达条目1低端和条目1顶端之间的位置,即条目2往上推动条目1的时候

        滑动的距离就应该是topDistance1-height和即topDistance1之间,我们做了如下的判断:

<pre name="code" class="java"><span style="font-size:18px;">if(distance>topDistance1-height && distance<topDistance1 ){
            title.layout(0,(int)(topDistance1-distance-height),title.getRight(),(int)(topDistance1-distance));
            showText(topDistance0);
        }</span>





       此时title是动态的,我们根据滑动的距离实时给title定个位。

        剩下的条目依次类推。

        把这两个判断做下封装,传入不同的距离值就可以啦!

        OK,demo完成!


源码:http://download.youkuaiyun.com/detail/aa_chao/9635014

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值