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

来源:互联网 发布:mac系统 high sierra 编辑:程序博客网 时间:2024/06/10 10:15

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

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

大致步骤:

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 0
原创粉丝点击