Android中实现StickyNavLayout自己的小demo

借鉴洪洋大神StickyNavLayout的demo加入自己的注解,方便以后查阅。

一.思绪整理:

1.要实现控件经包含的可滑动的view向上滑去使得头部隐藏,下面包含的view固定。拿到需求的第一想法是用scrollview去做,要这样的话可能要解决滑动的冲突的问题。呵呵这个常规的想法也没错就是实现有点麻烦。

2.网上百度了一下很多的大神已经实现的这种的demo,如洪洋大神的StickyNavLayout和玉刚大神的StickLayout都是很高的借鉴例子。

3.看了代码思路是:自定义view继承LinearLayout,获取子view的高度最重要是获取要隐藏topview,之后用在写滑动的逻辑代码。

图片效果:


1.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/ll"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <com.example.foreveross.myapplication.view.StickyNavLayout
        android:id="@+id/stickylayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="头部"
            android:textSize="40dp"/>

        <TextView
            android:id="@+id/ll_tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="固定部分"
            android:textSize="40dp"/>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/activity_recycleview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:divider="#ffff0000"
            android:dividerHeight="10dp">
        </android.support.v7.widget.RecyclerView>

    </com.example.foreveross.myapplication.view.StickyNavLayout>

</LinearLayout>


2.StickyNavLayout.java:

package com.example.foreveross.myapplication.view;

import android.content.Context;
import android.support.v4.view.NestedScrollingParent;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.OverScroller;

import com.example.foreveross.myapplication.R;


public class StickyNavLayout extends LinearLayout implements NestedScrollingParent
{

    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes)
    {
        return true;
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes)
    {
    }

    @Override
    public void onStopNestedScroll(View target)
    {
    }

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)
    {
    }

    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed)
    {
        //进行头部view是否隐藏的判断
        boolean hiddenTop = dy > 0 && getScrollY() < mTopViewHeight;
        boolean showTop = dy < 0 && getScrollY() >= 0 && !ViewCompat.canScrollVertically(target, -1);

        if (hiddenTop || showTop)
        {
            scrollBy(0, dy);
            consumed[1] = dy;
        }
    }

    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed)
    {
        return false;
    }

    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY)
    {
        //down - //up+
        if (getScrollY() >= mTopViewHeight) return false;
        fling((int) velocityY);
        return true;
    }

    @Override
    public int getNestedScrollAxes()
    {
        return 0;
    }

    private View mTop;
    private View mNav;
    private RecyclerView mRecyclerView;

    private int mTopViewHeight;             //顶部view滚动的高度

    private OverScroller mScroller;
    private VelocityTracker mVelocityTracker;           //速度
    private int mTouchSlop;
    private int mMaximumVelocity, mMinimumVelocity;

    private float mLastY;
    private boolean mDragging;

    public StickyNavLayout(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        setOrientation(LinearLayout.VERTICAL);

        mScroller = new OverScroller(context);
        //getScaledTouchSlop是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。
        // 如果小于这个距离就不触发移动控件
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();

        mMaximumVelocity = ViewConfiguration.get(context)
                .getScaledMaximumFlingVelocity();
        mMinimumVelocity = ViewConfiguration.get(context)
                .getScaledMinimumFlingVelocity();

    }

    private void initVelocityTrackerIfNotExists()
    {
        if (mVelocityTracker == null)
        {
            mVelocityTracker = VelocityTracker.obtain();
        }
    }

    private void recycleVelocityTracker()
    {
        if (mVelocityTracker != null)
        {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }

    /**
     * 当viewgroup填充完成后执行的
     */
    @Override
    protected void onFinishInflate()
    {
        super.onFinishInflate();
        mTop = findViewById(R.id.tv);           //可隐藏的头部
        mNav = findViewById(R.id.ll_tv);         //滑动上去可固定的
        View view = findViewById(R.id.activity_recycleview);       //listview
        mRecyclerView = (RecyclerView) view;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获取第一个子view把测量模式设为UNSPECIFIED
        getChildAt(0).measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        //获取RecyclerView的高度并设置高度
        ViewGroup.LayoutParams params = mRecyclerView.getLayoutParams();
        params.height = getMeasuredHeight() - mNav.getMeasuredHeight();
        //最终的测量设置StickyNavLayout的高度
        setMeasuredDimension(getMeasuredWidth(), mTop.getMeasuredHeight() + mNav.getMeasuredHeight() + mRecyclerView.getMeasuredHeight());

    }

    /**
     * 当view的大小改变时调用
     * @param w
     * @param h
     * @param oldw
     * @param oldh
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh)
    {
        super.onSizeChanged(w, h, oldw, oldh);
        mTopViewHeight = mTop.getMeasuredHeight();
    }


    public void fling(int velocityY)
    {
        mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, mTopViewHeight);
        invalidate();
    }

    /**
     * 滚动到位置的设置
     * @param x
     * @param y
     */
    @Override
    public void scrollTo(int x, int y)
    {
        if (y < 0)
        {
            y = 0;
        }
        if (y > mTopViewHeight)
        {
            y = mTopViewHeight;
        }
        if (y != getScrollY())
        {
            super.scrollTo(x, y);
        }
    }

    @Override
    public void computeScroll()
    {
        if (mScroller.computeScrollOffset())
        {
            scrollTo(0, mScroller.getCurrY());
            invalidate();
        }
    }
}


代码下载
感悟总结:

一个星期不知不觉就过了,生活就是一样白驹过隙一般。当自己全身心的投入到一件事情当中时虽然痛苦和折磨是伴随其中的,思想的反复挣扎,达不到预期的心情低落;但当全然豁然开朗时却把一切阴霾吹得烟消云散。

个人坚持和外部的压力,杂糅成一团乱码。就像BUG一样出现得不知所以然,而只有是越挫越勇之后的静下心来的梳理思绪才能理顺BUG的出处。

一个需求网上大神虽然有类似的demo可借鉴,而自己却急于实现而不去吸收理解。只求快速地实现功能。这是丢了西瓜拣芝麻的事,使得自己精髓没学到却出现一大堆bug,

以后切记着这好高骛远的想法,踏踏实实去消化吸收。


  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 平台上的自启动和保活功能是指应用程序在设备重启后能够自动启动,并且在后台保持长时间运行的能力。下面我将以一个实现示例来说明如何实现自启动和保活。 首先,我们需要在 AndroidManifest.xml 文件注册一个接收设备启动完成广播的广播接收器(BroadcastReceiver),如下所示: ```xml <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application> <!-- ... --> <receiver android:name=".BootCompletedReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> </application> ``` 然后,我们需要创建一个 BootCompletedReceiver 类来处理设备启动广播: ```java public class BootCompletedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { // 在设备启动完成时执行需要自启动的操作 // 例如启动一个服务、开启一个前台服务等 Intent serviceIntent = new Intent(context, YourService.class); context.startService(serviceIntent); } } } ``` 接下来,我们需要实现保活功能。一种常见的做法是通过在前台创建一个空的透明 Activity,然后在该 Activity 的 onCreate() 方法启动一个具备保活能力的服务。 ```java public class KeepAliveActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 设置 Activity 透明度为 0,使其不可见 Window window = getWindow(); window.setGravity(Gravity.LEFT | Gravity.TOP); WindowManager.LayoutParams params = window.getAttributes(); params.x = 0; params.y = 0; params.width = 1; params.height = 1; window.setAttributes(params); // 启动一个具备保活功能的服务 Intent serviceIntent = new Intent(this, KeepAliveService.class); startService(serviceIntent); } } ``` 最后,我们需要在 AndroidManifest.xml 文件注册这个保活 Activity: ```xml <activity android:name=".KeepAliveActivity" android:theme="@android:style/Theme.Translucent.NoTitleBar" android:excludeFromRecents="true" android:taskAffinity="" android:lockTaskMode="if_whitelisted" android:screenOrientation="portrait" /> ``` 通过以上步骤,我们就可以实现 Android 自启动和保活的功能了。当设备启动完成时,系统会发送 `ACTION_BOOT_COMPLETED` 广播,我们的应用程序将接收到该广播并启动自定义的服务,从而实现自启动。同时,在保活 Activity 的 onCreate() 方法启动保活服务,可以使应用程序在后台长时间保持运行状态。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值