Android 中上拉加载的解决方法(附带我的完整demo)(1)

本文分享了一种自定义上拉加载组件的实现方法,包括布局文件和关键代码。通过触摸事件判断用户行为,实现加载更多功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写在之前:

一年前当我第一次遇到了要上拉加载的时候,当时还是有一些信心的,但是很快我的信心被摧毁了,当时我做出的上拉加载一塌糊涂,完全啊没法看,实在是心塞的不行。之后又想到了使用网络上的框架,当时搜索了很多,有一个叫做廖枯秋的人写的框架,但是下载下来之后实在是看不进去,而且他的也没有写明白到底怎么去使用(当然,估计更多的原因是因为我实在是看不进去别人的程序),之后我就一个在用我写的一个,当时的我不知道进程池,不知道Asycntask,好吧虽然到现在还是不会写这个单词,单词是异步任务的意思,当然就是和主界面分开的意思。。其实每个人自己也可以利用handle写出一个Asynctask一样的东西,当然我自己到现在在实际中也没有体会到区别是什么,如果说有区别,那就是Asycntask更加方便使用,估计如果看了源码我就懂了。但是最近真的好心塞啊。找不到实习,上了大学之后,自以为自己很清高,,,实则穷秀才,听到别人都是内推的消息,简直是心塞啊,心塞之下,写了这篇

关于上拉加载 (一个是可以自己定制底部的东西的,一个是不能自己定制底部的,,,,我自己做的很丑)

首先:这是我自己写的底部加载页面

文件名字是:listview_footer.xml

很好理解,什么都没有,只有一个progressbar(转圈圈)一个textview(写现在是什么状态)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:layout_width="match_parent"
        android:orientation="horizontal"
        android:layout_height="40dp"
        android:background="#dadada"
        android:gravity="center">
        <ProgressBar
            android:layout_width="27dp"
            android:layout_height="27dp"
            android:layout_marginRight="5dp"
            android:id="@+id/pro"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:textSize="13sp"
            android:textStyle="bold"
            android:textColor="#9c9c9c"
            android:gravity="center"
            android:id="@+id/footer_text"
            android:text="正在刷新..."/>
    </LinearLayout>
</LinearLayout>

这是我的不能定制底部的, 可以下载了直接使用,当然记得改一次前面的部分,这个类使用了前面的

脚部布局,可以直接复制了使用,在下面我会粘贴上我自己使用的代码

package com.chunni.android.chunni.tool;


import android.content.Context;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.chunni.android.chunni.R;

import java.util.concurrent.ExecutorService;


/**
 * Created by 蒲公英 on 2017/5/31.
 */

public class ListViewUpload2 extends ListView {


    //开始加载的标识符号
    private final int LOAD = 100002;
    //如果加载失败,比如你去网上获取内容了,失败了
    private final int FAIL = 100007;


    //一步加载几个
    private int STEP = 0;

    //附带自己线程池的初始化,就是你自己定义了线程池,让Ascnytask在你的里面运行

    /**
     * 附带自己线程池的初始化,就是你自己定义了线程池,让Ascnytask在你的里面运行
     * @param doload 接口(因为要根据这里的进度去操作另外的页面)
     * @param STEP  一步加载的数量
     * @param executors 你自己定义的线程池
     */
    public void initInPool(Doload doload, int STEP, ExecutorService executors) {
        this.doload = doload;
        this.STEP = STEP;
        this.executors = executors;
        addFooter();
        set_foot(1);
        new MyAsyncTask().executeOnExecutor(this.executors);
    }

    //没有自己线程池的初始化

    /**
     * 初始化
     * @param doload 接口
     * @param STEP  一步的数量
     */
    public void init(Doload doload, int STEP) {
        this.doload = doload;
        this.STEP = STEP;
        addFooter();
        set_foot(1);
        new MyAsyncTask().execute();
    }

    //线程池
    private ExecutorService executors = null;

    //如果(数据加载失败)时候你的load应该返回这个值
    public static final int BADDATA = -1;

    //下面的转圈圈
    private ProgressBar pro = null;


    //标记是不是还能进行加载
    private boolean CAN = false;

    //起点
    private float start = 0;

    //最大高度 测量得到
    private int highest = 0;

    //大小设置工具
    private LayoutParams layoutParams = null;

    //底部布局
    private LinearLayout footer = null;

    //底部文字
    private TextView text_footer = null;

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

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

    public ListViewUpload2(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    /**
     * load :加载的时候执行的内容,返回值是这一次加载的数量,如果网络请求失败,没有加载到,返回BADDATA(-1)
     * finish:加载完成时候的操作,就是这里加载完了,那边要执行的操作
     */
    public interface Doload{
        int load();
        void finish();
    }

    //实例化接口
    private Doload doload;

    /**
     * 主要的代码:说一下想法,如果CAN为true,那么就去并且已经滑动到最后了,那么按下才有加载
     *              这个时候就已经确定了start也就是你开始的地方
     *              然后开始滑动了,你移动的时候,根据你上拉的程度,不断地刷新底部view的高度,
     *              之后手指松开的时候,如果松开的位置-开始的位置 大于 底部的高度,可以加载,然后去执行Asynctask
     * @param ev
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                if (this.getLastVisiblePosition() == this.getCount() - 1 && CAN)
                {
                    start = ev.getY();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (start > 0)
                {
                    float move = ev.getY() - start;
                    if (move < 0 && move > -highest){
                        set_foot((int) -move);
                        text_footer.setText("上拉加载");
                    }
                    if (move < -highest) {
                        set_foot(highest);
                        text_footer.setText("松开加载");
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                if (start > 0) {
                    if ((ev.getY() - start) < -highest) {
                        if (this.executors == null){
                            new MyAsyncTask().execute();
                        }else{
                            new MyAsyncTask().executeOnExecutor(this.executors);
                        }
                    }
                }
                break;
        }
        return super.onTouchEvent(ev);
    }


    /**
     * 设置底部高度为1,解决了好多设置不可见引起的问题。。
     * @param height
     */
    private void set_foot(int height){
        layoutParams.height = height;
        footer.setLayoutParams(layoutParams);
        Log.i("xjxu" , height + " " + footer.getHeight());
    }

    /**
     * 自定义的异步任务
     */
    private class MyAsyncTask extends AsyncTask<Void, Integer, Integer> {

        @Override
        protected Integer doInBackground(Void... objects) {
            publishProgress(LOAD);
            int sign = doload.load();
            if (sign == -1){
                publishProgress(FAIL);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return sign;
        }

        @Override
        protected void onProgressUpdate(Integer... pros) {
            super.onProgressUpdate(pros);
            switch (pros[0]){
                case LOAD:
                    CAN = false;
                    start = 0;
                    set_foot(highest);
                    text_footer.setText("加载中");
                    break;
                case FAIL:
                    set_foot(highest);
                    pro.setVisibility(GONE);
                    text_footer.setText("请检查你的网络连接");
                    break;
            }
        }

        @Override
        protected void onPostExecute(Integer count) {
            super.onPostExecute(count);
            if (count == STEP || count == BADDATA){
                pro.setVisibility(VISIBLE);
                CAN = true;
                set_foot(1);
                return;
            }
            else{
                CAN = false;
                set_foot(highest);
                text_footer.setText("已经没有更多内容了");
                pro.setVisibility(GONE);
            }
            doload.finish();
        }
    }

    /**
     * 添加底部
     */
    private void addFooter(){
        footer = (LinearLayout) LayoutInflater.from(this.getContext()).inflate(R.layout.listview_footer, null);
        //最后一次参数的意思是 是不是可以选择到,如果是true,点了会触发onitemclick
        this.addFooterView(footer, null, false);
        this.setFooterDividersEnabled(false);

        text_footer = footer.findViewById(R.id.footer_text);

        pro = footer.findViewById(R.id.pro);

        //测量
        footer.measure(0 , 0);

        highest = footer.getMeasuredHeight();

        //初始化param
        layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);

        set_foot(1);
    }
}

我自己使用的实际例子

这是布局,注意如果你直接复制了我的布局和类 注意更改包名哦

<com.chunni.android.chunni.tool.ListViewUpload2
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none"
        android:visibility="gone"
        android:divider="@null"
        android:id="@+id/lst">
</com.chunni.android.chunni.tool.ListViewUpload2>
这是我使用的代码
/**
     * 初始化上拉加载控件
     */
    private void initUpload(){
        listViewUpload.setAdapter(uploadAdapter = new UploadAdapter());
        listViewUpload.init(new ListViewUpload2.Doload() {
            @Override
            public int load() {
                String result = new NetWorkOut().outPost(App.SERVER + "article/artList?" +
                        "userId=" + App.USERPHONE +
                        "&start=" + uploads.size() +
                        "&end=" + (uploads.size() + 5));
                if (result == null)
                    return -1;
                JsonArray  jsonArray = jsonParser.parse(result)
                        .getAsJsonObject()
                        .get("data_upload")
                        .getAsJsonArray();
                if (uploads == null)
                    uploads = new JsonArray();
                uploads.addAll(jsonArray);
                return jsonArray.size();
            }

            @Override
            public void finish() {
                uploadAdapter.notifyDataSetChanged();
            }
        }, 5);

        load();
    }

写在最后,一直以为自己写的很短,但是贴出来还是发现很长,所以我可能会之后上传一个github的连接,当然现在的我一个github也没有发过,。。。。最后的最后,如果有什么问题或者建议请写在下面好吗,亲们^_^

也希望能给像当初的我一样的人一点帮助,当然如果大神看到了,还请多多指教。。。

最后祝福自己。。。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值