功能效果展示

Android RecyclerView滑动时闪退 recyclerview点击跳转activity_java

界面设计分析

上述APP界面分为两个部分,第一部分是新闻列表页,第二部分是新闻详情页,新闻列表页中可以使用recyclerView实现,详情页这里我使用了HtmlTextView接受url传回的html数据,进行显示。具体如下:

新闻详情页

此次在微信tab页添加列表,因此可以直接在fragment_news.xml文件中设置

  • 添加RecyclerView控件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".news">
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
  • 创建recyclerView的布局

对每个item来说,都包含图片,新闻标题,新闻来源,以及发布时间,因此可以做以下布局。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/liner1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <ImageView
        android:id="@+id/news_image"
        android:layout_width="140dp"
        android:layout_height="100dp"
        android:layout_weight="0"
        tools:srcCompat="@tools:sample/avatars" />
    <LinearLayout
        android:id="@+id/liner2"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="0"
        android:orientation="vertical">
        <TextView
            android:id="@+id/news_title"
            android:layout_width="match_parent"
            android:layout_height="80dp"
            android:text="标题"
            android:textSize="20sp" />
        <LinearLayout
            android:id="@+id/liner3"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <TextView
                android:id="@+id/news_author"
                android:layout_width="wrap_content"
                android:layout_height="20dp"
                android:textSize="16sp"
                android:layout_weight="1"
                android:text="作者" />
            <TextView
                android:id="@+id/news_date"
                android:layout_width="wrap_content"
                android:layout_height="20dp"
                android:textSize="16sp"
                android:layout_weight="1"
                android:text="发布时间" />
        </LinearLayout>
    </LinearLayout>
</LinearLayout>
  • 效果展示

Android RecyclerView滑动时闪退 recyclerview点击跳转activity_安卓_02

新闻详情页

新闻详情页比较简单,因为我的详情页面的数据是由okhttp根据新闻url请求后返回的HTML数据构成,因此选择htmlTextView作为控件解析页面。

  • 布局文件
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="ContentActivity">
    <org.sufficientlysecure.htmltextview.HtmlTextView
        android:id="@+id/html_text"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:textAppearance="@android:style/TextAppearance.Small" />
</ScrollView>

功能实现方法

RecyclerView的控制

Adapter的设计
  • adapter中主要完成的任务是如何将前端传来的json数据正确的填充到RecyclerView的每一个控件中,因此要将对应数据从list中取出,然后通过setText方法填入。
public class News_adapter extends RecyclerView.Adapter <News_adapter.ViewHolder> {

    public List<Map<String, Object>> list = new ArrayList<>();
    public Context con;
    private AdapterView.OnItemClickListener onItemClickListener;

    public News_adapter(List<Map<String,Object>> list, Context con) {
        this.con=con;
        this.list=list;
    }
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false);
        ViewHolder viewHolder=new ViewHolder(view);
        return viewHolder;
    }
    @Override
    public void onBindViewHolder(final ViewHolder holder, @SuppressLint("RecyclerView") final int position) {
        String test = list.get(position).get("title").toString();
        holder.recy_title.setText(list.get(position).get("title").toString());
        holder.recy_date.setText(list.get(position).get("date").toString());
        Glide.with(holder.recy_imageView.getContext()).load(list.get(position).get("thumbnail_pic_s")).into(holder.recy_imageView);
        holder.recy_author.setText(list.get(position).get("authors").toString());
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Bundle bundle = new Bundle();
                try {
                    bundle.putCharSequence("url",list.get(position).get("url").toString());
                    Intent intent = new Intent(con, ContentActivity.class);
                    intent.putExtras(bundle);
                    con.startActivity(intent);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
    @Override
    public int getItemCount() {
        return list.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        public TextView recy_title;
        public ImageView recy_imageView;
        public TextView recy_author;
        public TextView recy_date;
        public ViewHolder(View itemView) {
            super(itemView);
            recy_title = itemView.findViewById(R.id.news_title);
            recy_imageView = itemView.findViewById(R.id.news_image);
            recy_author = itemView.findViewById(R.id.news_author);
            recy_date = itemView.findViewById(R.id.news_date);
        }
    }
    public interface OnItemClickListener{
        void onItemClick(int position);
    }
    //点击方法
    public void setOnItemClickListener(OnItemClickListener onItemClickListener){
        this.onItemClickListener = (AdapterView.OnItemClickListener) onItemClickListener;
    }
}
数据的解析和Adapter的使用
  • 数据解析

此次新闻数据我使用了聚合新闻网的API来返回相应的json数据,在news.java中进行解析

Android RecyclerView滑动时闪退 recyclerview点击跳转activity_android_03

  • 数据处理
//okhttp获取数据
private void okhttpDate() {
        Log.i("okk", "--ok-");
        new Thread(new Runnable() {
            @Override
            public void run() {
                OkHttpClient client = new OkHttpClient();
                Request request = new Request.Builder().url(URL).build();
                try {
                    Response sponse = client.newCall(request).execute();
                    data = sponse.body().string();
                    //解析
                    jsonJXDate(data);
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }).start();

    }
   //对返回的json数据进行解析,重新写入list
private void jsonJXDate(String date) {
        if(date!=null) {
            try {
                JSONObject jsonObject = new JSONObject(date);
                //转对象
                JSONObject jsonObject1 = jsonObject.getJSONObject("result");
                //对象转数组用来遍历
                JSONArray jsonArray = jsonObject1.getJSONArray("data");
                //遍历数组
                for (int i = 0; i < jsonArray.length(); i++) {
                    //将字段的值遍历并转型
                    String title = jsonArray.getJSONObject(i).getString("title");
                    String url = jsonArray.getJSONObject(i).getString("url");
                    String news_date = jsonArray.getJSONObject(i).getString("date");
                    String news_author = jsonArray.getJSONObject(i).getString("author_name");
                    String thumbnail_pic_s = jsonArray.getJSONObject(i).getString("thumbnail_pic_s");
					//放入集合中,根据需要是否做下面的操作
                    Map<String, Object> map = new HashMap<>();
                    map.put("title", title);
                    map.put("url", url);
                    map.put("date", news_date);
                    map.put("authors",news_author);
                    map.put("thumbnail_pic_s",thumbnail_pic_s);
                    news_list.add(map);
                }
                Message msg=new Message();
                msg.what=1;
                handler.sendMessage(msg);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }
    //定义handler
    public Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 1:
                    //添加分割线
                    recyclerview.addItemDecoration(new DividerItemDecoration(
                            context, DividerItemDecoration.VERTICAL));
                    News_adapter recy=new News_adapter(news_list,context);
                    //设置布局显示格式
                    recyclerview.setLayoutManager(new LinearLayoutManager(context));
                    recyclerview.setAdapter(recy);
                    break;


            }
        }
    };
  • 打开网络权限
<uses-permission android:name="android.permission.INTERNET" />
  • 安卓规定不能在主进行网络请求等耗时操作,于是需要在子线程中进行耗时操作,之后提交请求,让主线程做UI更新。因此引入Handler,Handler是SDK中处理异步消息的核心类。主要接受子线程发送的数据, 并用此数据配合主线程更新UI。
  • 通过Handler将获取到的数据填入list,并通过Adapter创建的对象映射到每个控件上。

Activity的跳转

这里的content_activity较为简单,仅展示代码

public class ContentActivity extends AppCompatActivity {
    protected String response = null;
    private String url = null;
    protected HtmlTextView htmlTextView;
    Handler handler = new Handler()
    {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what==12)
            {
                htmlTextView.setHtml(response, new HtmlHttpImageGetter(htmlTextView));
            }
        }
    };
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_content);
        htmlTextView = findViewById(R.id.html_text);
        final Intent intent = getIntent();
        Bundle bundle = intent.getExtras();
        url = bundle.getCharSequence("url").toString();
        getHTML(url);
        //html内容转化
    }
    public void getHTML(String aUrl){
        new Thread(new Runnable() {
            @Override
            public void run() {
                OkHttpClient client = new OkHttpClient();
                Request request = new Request.Builder().url(aUrl).build();
                try {
                    Response sponse = client.newCall(request).execute();
                    response = sponse.body().string();
                    Message msg=new Message();
                    msg.what=12;
                    handler.sendMessage(msg);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
  • 想要从新闻列表页跳转至新闻详情页,首先要对点击动作做监听。
public interface OnItemClickListener{
        void onItemClick(int position);
    }
    //点击方法
    public void setOnItemClickListener(OnItemClickListener onItemClickListener){
        this.onItemClickListener = (AdapterView.OnItemClickListener) onItemClickListener;
    }
  • 然后实现跳转,这里使用类名进行跳转,需要在AndroidManifest.xml中注册
<activity android:name=".ContentActivity"/>
holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Bundle bundle = new Bundle();
                try {
                    bundle.putCharSequence("url",list.get(position).get("url").toString());
                    Intent intent = new Intent(con, ContentActivity.class);
                    intent.putExtras(bundle);
                    con.startActivity(intent);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

遇到的问题以及解决办法

数据解析问题

  • 从Android4.0开始,Android不允许再主线程中进行网络请求,不允许在子线程中进行UI更新。但是我们在子线程中从服务端获取到的最新的数据怎么传递给主线程被用来更新UI呢?这就需要用到Handler机制。
  • 如果直接进行线程异步请求网络,最终list中不会有更新的数据。

新闻详情页面数据问题

  • 从聚合新闻API返回的新闻详情内容只有对应的URL,因此在点击的同时要请求URL对应的资源并进行解析渲染,在此可以同上面使用相同的方法,利用okhttp发起请求,接受返回的数据,异步填入HtmlTextView控件中即可
public void getHTML(String aUrl){
        new Thread(new Runnable() {
            @Override
            public void run() {
                OkHttpClient client = new OkHttpClient();
                Request request = new Request.Builder().url(aUrl).build();
                try {
                    Response sponse = client.newCall(request).execute();
                    response = sponse.body().string();
                    Message msg=new Message();
                    msg.what=12;
                    handler.sendMessage(msg);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        
 Handler handler = new Handler()
    {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what==12)
            {
                htmlTextView.setHtml(response, new HtmlHttpImageGetter(htmlTextView));
            }
        }
    };
  • 未解决的问题 返回的HTML页面中,图片的地址为:src=//dfzximg01.dftoutiao.com/news/…png 导致控件无法解析图片,即出现文章最开始演示部分的内容,只有文字,没有图片。

Android RecyclerView滑动时闪退 recyclerview点击跳转activity_java_04

代码仓库地址

gitee仓库地址