listview滑动到底部弹出按钮-button占用了listview的显示位置(尽力理解尽力解决)

今天在写一个程序。从接口获取数据解析后显示到自己的布局中:

先不说出现的问题先,总结一下这样一个程序的大体逻辑思路---->

大致步骤:

1:编写主布局(添加listview)

2:自定义listview的样式

:3:自定义适配器:BaseAdapter    //这个过程可先伪造一些数据源进行调试,检查自己的程序

4:创建异步任务(AsyncTask)

o在doInBackground(该方法子线程执行)方法中执行网络访问的耗时操作(可使用httpurlconnection、okhttp)

o将得到的数据进行解析:可在doInBackground()也可在onPostExecute()方法中解析,并将解析的数据作为付给布局中数据源

5:使用适配器和数据源

问题:加载第一页listview时,设置在底部visiable状态为gone的按钮;占用了listview的位置:

效果图:

(存在问题:第一页加载时有空白,留意底部有一个空白占用着位置,但滑动之后空白消失)


解决后图片:


最后附上在底部弹出按钮的效果图:


上程序代码;

1:主布局的xml文件

<?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"

    tools:context="com.example.administrator.qiubai10.MainActivity">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="加载更多..."
        android:background="@color/colorAccent"
        android:id="@+id/btn_loadmore"
        android:layout_alignParentBottom="true"
        android:visibility="gone"/>
    <ListView
        android:layout_above="@id/btn_loadmore"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/listview"></ListView>
</RelativeLayout>

2:自定义listview的样式:(只有一个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="wrap_content">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textview_content"/>
</LinearLayout>
<--开发中使用相对布局的较多,因为在实现复杂界面时需要多层的嵌套,过度的渲染,而且在作修改时对其他的控件影响较大,但相对布局的控件等是较为独立的->

3:自定义适配器:

package com.example.administrator.qiubai10;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.util.List;

/**
 * Created by Administrator on 2016/9/1.
 */
public class Myadapter extends BaseAdapter {
    List<QiuBai> list ;
    Context context ;
    LayoutInflater inflater ;

    public Myadapter(List<QiuBai> list, Context context) {
        this.list = list;
        this.context = context;
        this.inflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder = null ;
        if (convertView == null) {
            viewHolder = new ViewHolder();
            convertView = inflater.inflate(R.layout.listview_item, parent, false);
            viewHolder.content = (TextView) convertView.findViewById(R.id.textview_content);
            convertView.setTag(viewHolder);
        }else{
            viewHolder  = (ViewHolder)convertView.getTag();
        }
        if(position%2==0){        //这里是为了相邻两个textview不同的背景颜色
            convertView.setBackgroundColor(context.getResources().getColor(R.color.colorHui));

        }else{
            convertView.setBackgroundColor(context.getResources().getColor(R.color.colorWhite));
        }
        QiuBai qiuBai = list.get(position);
        viewHolder.content.setText(qiuBai.getContent());
        return convertView;
    }
    class ViewHolder{
        TextView content ;
    }
}

上面出现的 QiuBai 的类是我用来保存网络下载的数据对应的属性。后面我的数据源就是我的 QiuBai 对象的集合(QiuBai的代码在最后贴上)

4:主类(异步任务类为其内部类,方便访问主类的属性)

该类完成网络请求数据,对数据解析,初始化数据源,并且设置listview的适配器。

package com.example.administrator.qiubai10;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.AbsListView;
import android.widget.Button;
import android.widget.ListView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    List<QiuBai> list = new ArrayList<QiuBai>();
    private ListView listview;
    private Button btn_load;
    static int page =1;
    String path = "http://m2.qiushibaike.com/article/list/suggest?page=%s";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listview = ((ListView) findViewById(R.id.listview));
        btn_load = ((Button) findViewById(R.id.btn_loadmore));
        String url = String.format(path,String.valueOf(page));
        new MyTask().execute(url);
        btn_load.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        if(v.getId()==R.id.btn_loadmore){
            String url = String.format(path,String.valueOf(++page));
            Log.d("bigname_log", "onClick: "+url);
            new MyTask().execute(url);
        }
    }

    //异步任务类:请求网络数据,解析后设置到list中
    class MyTask extends AsyncTask<String, Void, List<QiuBai>> {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected List<QiuBai> doInBackground(String... params) {
            byte[] bytes = HttpUtils.getDatas(params[0]);
            String data = new String(bytes, 0, bytes.length);
            List<QiuBai> qiuBais = parseJson(data);
            list.addAll(qiuBais);
            return list;
        }

        @Override
        protected void onPostExecute(List<QiuBai> qiuBais) {
            Log.d("bigname_log", "onPostExecute: 加载适配器");//**************************************************************
            listview.setAdapter(new Myadapter(list, MainActivity.this));
            listview.setOnScrollListener(new AbsListView.OnScrollListener() {
                /*
                * 1  abslistview view:
                * listview对象
                * 2  int scrollstate:
                * listview的状态,有三种:
                * 0:停止
                * 1:滑动
                * 2:抛掷
                * */
                @Override
                public void onScrollStateChanged(AbsListView view, int scrollState) {
                    Log.d("bigname_log", "onScrollStateChanged: "+scrollState);
                }
                /*
                * abslistview view:   listview对象
                * int firstvisiableitem:   第一个可见item的位置
                * int totalItemcount:   总的item个数(长度)
                * int visibleitemcount:  可见item的个数
                * **/
                @Override
                public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                    Log.d("bigname_log", "onScroll: 测量");       //*****************************************
                    if ((firstVisibleItem + visibleItemCount) == totalItemCount) {
                        Log.d("bigname_log", "onScroll:  等于总"); //********************************************
                        btn_load.setVisibility(View.VISIBLE);
                        //此时的button悬浮在listview的上方,(覆盖)。应该是因为布局更新的问题。可调用listview的requestlayout()来重新加载
                    }else{
                        Log.d("bigname_log", "onScroll:  不等于总");//****************************************
                        btn_load.setVisibility(View.GONE);
                        listview.requestLayout();
                    }
<span style="white-space:pre">			</span>//????????????????????????????????????
                }
            });

           // Log.d("bigname_log", "onPostExecute: ---加载适配器-----");
        }
    }
//写成静态方法,方便其他的类使用
    private static List<QiuBai> parseJson(String data) {
//        将每次访问的一页数据保存在page_list中,返回在添加到总的list中,这样在点击加载更多的时候之前的数据也还在
        List<QiuBai> page_list = new ArrayList<QiuBai>();
        try {
            JSONObject jo = new JSONObject(data);
            JSONArray items = jo.getJSONArray("items");
            for (int i = 0; i < items.length(); i++) {
                JSONObject datas = items.getJSONObject(i);
                String id = datas.getString("id");
                String content = datas.getString("content");
                page_list.add(new QiuBai(content, id));
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return page_list;
    }
}



如标题所说:listview滚动到达底部后,按钮弹出,但还是占了listview位置的问题。

请留意//******************************对应代码的位置。

setAdapter()在前,onScroll()在后,

这样的代码是不会出现这个问题的,但我之前是:onScroll()在前,setAdapter()在后。这样就出现了第一个页面button的位置占了listview的位置。活动一下才消失。


原因应该是listview绘制的问题。在第一次加载的时候不论你是否滑动了,他都会去调用一次onScroll()方法,如果此时的setAdapter()还没把数据设置好的话他就会预留这个位置。虽然我在activity_main.xml中已经把button设为gone了。

有一点我很不理解:为什么跟我onScroll()写的位置有关,不是我写哪里都可以,滑动一次被监听到才执行的吗?虽然结果确实有关,但却不知道为什么?

--》先说明一下什么时候才回去第一次调用onScroll方法:

度娘告诉我:


可是,我们会发现,当运行程序时,listview明明没有滚动,那为什么系统会调用onScroll方法呢?(补充:此时onScrollStateChanged并不会调用)

我们先看setOnScrollListener源码:

public void setOnScrollListener(OnScrollListener l) {   //当你在设置监听的时候里面一般传一个匿名内部类,此时l就等于你那个匿名内部类,也就是说l已经不为空                                         
        mOnScrollListener = l;
        invokeOnItemScrollListener();
    }
setOnScrollListener里面调用了invokeOnItemScrollListener()方法,接着看该方法源码: 
void  invokeOnItemScrollListener() {
        if (mFastScroller != null) {
             mFastScroller.onScroll(mFirstPosition, getChildCount(), mItemCount);
         }
         if (mOnScrollListener != null) {
            mOnScrollListener.onScroll(this, mFirstPosition, getChildCount(), mItemCount);//这里调用onScroll,一切真相大白了。
         }
         onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these.
       }



所以:!setScrollListener()的设置真的跟你设置的位置有关系的,现在终于弄懂了一点,也就是说,你在哪写的setOnscrollListener(),onScroll就会在哪开始执行一次。也就是说,你只要设置了,Scroll就会去执行。

好了接着继续分析:

下面来猜测一下:

程序先从onCreate()方法中进入布局,接着调用setContentView(R.layout.activity_main)这是后就是把布局加载到activity中去,但此时的listview 没有数据并且button也设置为gone,所以在此时的页面没东西显示的,然后执行了异步任务,下载数据。。。

到了onPostExecute()方法时(此时把数据都下载好,并解析存到数据源中):

按照如上的代码,把setAdapter()卸载setOnScrollListener()前面;打印的结果:

加载适配器

测量

不等于总

然后把setAdapter()移到//????????的那个位置也就是setAdapter()在setOnScrollListener()后面,打印结果

测量

等于总

加载适配器

测量

不等于

②时:

因为-等于总-所以会将button控件设为visiable可见,这时候就会占了底下的位置,也就是说button确实显示出来了(只是时间太短,你没感觉到),很快这时候再加载适配器把数据显示出来,这时候又调用了一次onscoll()方法,发现不等于,马上又把button设为gone了。此时数据已经加载到那里了,再调用requsetLayout()吧button去掉也没用了。(这里关键是button先设为visiable再变为gone这个过程引发的问题)

①时:

也能够说的通:先加载适配器,把数据都显示到了listview中,再写setOnScrollListener()方法,此时判断为--不等于--所以button依然是gone的状态。。。

哈哈,到这就差不多完美结束这篇文章了。本来写这篇文章是想简单记录一下出现的这个问题和解决的办法,至于不理解也就算了,但写着写着就忍不住去弄懂背后的原因。本人还是小菜鸟,这篇文章肯定还存在很多漏洞,希望看到的能够指出来!

花了一晚上的时间,效率低,但挺开心的,毕竟好像是弄明白了这个东东。哈哈。。

</pre><p></p><div style="top: 3571px;"><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; color: rgb(51, 51, 51); font-family: Helvetica, Arial, sans-serif; line-height: 19.44px;">可是,我们会发现,当运行程序时,listview明明没有滚动,那为什么系统会调用onScroll方法呢?(补充:此时onScrollStateChanged并不会调用)</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; color: rgb(51, 51, 51); font-family: Helvetica, Arial, sans-serif; line-height: 19.44px;">我们先看setOnScrollListener源码:</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; color: rgb(51, 51, 51); font-family: Helvetica, Arial, sans-serif; line-height: 19.44px;"></p><pre class="java" style="margin-top: 0px; margin-bottom: 0px; padding: 0px; color: rgb(51, 51, 51); line-height: 19.44px;">public void setOnScrollListener(OnScrollListener l) {
        mOnScrollListener = l;
        invokeOnItemScrollListener();
    }
setOnScrollListener里面调用了invokeOnItemScrollListener()方法,接着看该方法源码: 
void  invokeOnItemScrollListener() {
        if (mFastScroller != null) {
             mFastScroller.onScroll(mFirstPosition, getChildCount(), mItemCount);
         }
         if (mOnScrollListener != null) {
            mOnScrollListener.onScroll(this, mFirstPosition, getChildCount(), mItemCount);//这里调用onScroll,一切真相大白了。
         }
         onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these.
       }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现ListView滑动删除的功能,可以采用以下步骤: 1. 首先创建一个ListView,并给ListView设置一个适配器; 2. 在适配器的getView()方法中,为每一个item添加一个滑动删除的功能; 3. 在item中添加一个布局,该布局包含两个子控件:一个是要显示的内容,另一个是删除按钮; 4. 给删除按钮添加点击事件,当点击删除按钮时,将该item从ListView中移除; 5. 在ListView的OnTouchListener中,监听手势滑动事件,并实现滑动删除的效果。 下面是一个简单的实现代码: ``` public class MyAdapter extends BaseAdapter implements View.OnTouchListener { private Context mContext; private List<String> mData; private int mLastPosition; private int mDownX, mDownY; private boolean isSlide = false; public MyAdapter(Context context, List<String> data) { mContext = context; mData = data; } @Override public int getCount() { return mData.size(); } @Override public Object getItem(int position) { return mData.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if (convertView == null) { convertView = LayoutInflater.from(mContext).inflate(R.layout.item_layout, null); viewHolder = new ViewHolder(); viewHolder.contentTv = convertView.findViewById(R.id.content_tv); viewHolder.deleteBtn = convertView.findViewById(R.id.delete_btn); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.contentTv.setText(mData.get(position)); viewHolder.deleteBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mData.remove(position); notifyDataSetInvalidated(); } }); convertView.setOnTouchListener(this); convertView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 点击事件 } }); return convertView; } static class ViewHolder { TextView contentTv; Button deleteBtn; } @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: mDownX = (int) event.getX(); mDownY = (int) event.getY(); mLastPosition = v.getId(); isSlide = false; break; case MotionEvent.ACTION_MOVE: int moveX = (int) event.getX(); int moveY = (int) event.getY(); int deltaX = moveX - mDownX; int deltaY = moveY - mDownY; if (Math.abs(deltaX) > Math.abs(deltaY)) { isSlide = true; // 滑动事件 } break; case MotionEvent.ACTION_UP: if (!isSlide) { // 点击事件 } break; } return true; } } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值