一、说在前面
昨天 |
--------------- |
今天 |
1、环境搭建;2、设计思路 |
遇到问题 |
请求到的参数的出现中文乱码问题 |
二、分析新闻列表界面包含的功能
1、下拉搜索提示框。1.5h
2、发布自己身边的“新闻”。1h
3、置顶、最热新闻图片轮播。0.7h
4、新闻列表(置顶、下拉刷新、点击事件)0.8h
三、冲刺成果
2、新闻列表和点击事件:
一、设计思路
1、数据源:新闻api:https://www.jianshu.com/p/c54e25349b77。
2、网络请求:封装一个网络请求工具(使用HttpURLConnection)发送URL,返回数据string。
3、数据获取:使用 AsyncTask 异步加载网络数据,及调用工具类的工具函数。
4、数据解析:根据返回的数据和需要的数据,处理从网上获取的字符串,并使用Gson工具将处理后的字符串转换为我们熟悉的java对象。
5、网络图片展示:自定义一个imageView,用于展示网络图片。(我的是:传入一个网络图片的网址->展示该网络图片)
6、数据展示:使用RecycleView展示列表数据(包括新闻图片、标题、概要、时间)。
7、分析可知需添加依赖:
dependencies { implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'com.google.code.gson:gson:2.8.6' }
二、效果
三、源代码
1、用于发送网络请求的工具类:
package com.me.androidstudy2.utils; import android.util.Log; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; public class HttpUtil { /** * 返回json * @param setUrl * @return */ public static String setUrl(String setUrl){ try { URL url = new URL(setUrl); HttpURLConnection conn = (HttpURLConnection)url.openConnection(); conn.setConnectTimeout(5000); conn.setRequestMethod("GET"); int responseCode = conn.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK){ InputStream inputStream = conn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader reader = new BufferedReader(inputStreamReader); StringBuffer stringBuffer = new StringBuffer(); String string = reader.readLine(); while (string != null) { stringBuffer.append(string); string = reader.readLine(); } Log.e("web",stringBuffer.toString()); return stringBuffer.toString(); }else{ Log.e("web",responseCode+""); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return ""; } }
2、自定义一个imageView,用于展示网络图片。
package com.me.androidstudy2.view; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.widget.ImageView; import android.widget.Toast; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; /** * 使用时只需调用setImageURL(图片网址);即可 */ @SuppressLint("AppCompatCustomView") public class MyImageView extends ImageView { public static final int GET_DATA_SUCCESS = 1; public static final int NETWORK_ERROR = 2; public static final int SERVER_ERROR = 3; //子线程不能操作UI,通过Handler设置图片 private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what){ case GET_DATA_SUCCESS: Bitmap bitmap = (Bitmap) msg.obj; setImageBitmap(bitmap); break; case NETWORK_ERROR: Toast.makeText(getContext(),"网络连接失败",Toast.LENGTH_SHORT).show(); break; case SERVER_ERROR: Toast.makeText(getContext(),"服务器发生错误",Toast.LENGTH_SHORT).show(); break; } } }; public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public MyImageView(Context context) { super(context); } public MyImageView(Context context, AttributeSet attrs) { super(context, attrs); } //设置网络图片 public void setImageURL(final String path) { //开启一个线程用于联网 new Thread() { @Override public void run() { try { //把传过来的路径转成URL URL url = new URL(path); //获取连接 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); //使用GET方法访问网络 connection.setRequestMethod("GET"); //超时时间为10秒 connection.setConnectTimeout(10000); //获取返回码 int code = connection.getResponseCode(); if (code == 200) { InputStream inputStream = connection.getInputStream(); //使用工厂把网络的输入流生产Bitmap Bitmap bitmap = BitmapFactory.decodeStream(inputStream); //利用Message把图片发给Handler Message msg = Message.obtain(); msg.obj = bitmap; msg.what = GET_DATA_SUCCESS; handler.sendMessage(msg); inputStream.close(); }else { //服务启发生错误 handler.sendEmptyMessage(SERVER_ERROR); } } catch (IOException e) { e.printStackTrace(); //网络连接错误 handler.sendEmptyMessage(NETWORK_ERROR); } } }.start(); } }
3、activity的逻辑代码
package com.me.androidstudy2; import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.google.gson.Gson; import com.me.androidstudy2.adapter.NewAdapter; import com.me.androidstudy2.domain.News; import com.me.androidstudy2.utils.HttpUtil; public class XwActivity extends Activity { private News news = new News(); private RecyclerView recyclerView; private SentUrlTask sentUrlTask; private NewAdapter adapter = new NewAdapter(); public void initView(){ recyclerView = findViewById(R.id.rv_news); } public void action(){ sentUrlTask = new SentUrlTask("http://3g.163.com/touch/reconstruct/article/list/BBM54PGAwangning/0-20.html"); sentUrlTask.execute(); } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_xw); initView(); action(); } @Override protected void onDestroy() { super.onDestroy(); if (sentUrlTask != null) { sentUrlTask.cancel(true); } } private class SentUrlTask extends AsyncTask<Void,Void,String> { private String url; public SentUrlTask(String url) { this.url = url; } @Override protected String doInBackground(Void... voids) { return HttpUtil.setUrl(url); } @Override protected void onPostExecute(@NonNull String s) { super.onPostExecute(s); Gson gson = new Gson(); String ss = s.substring(9,s.length()-1); news = gson.fromJson(ss, News.class); adapter.setData(news.getBBM54PGAwangning()); recyclerView.setAdapter(adapter); recyclerView.setLayoutManager(new LinearLayoutManager(XwActivity.this)); recyclerView.setAdapter(adapter); Log.e("new-ss:",news.getBBM54PGAwangning().get(0).getTitle()); } } }
4、新闻列表的适配器:
1)MyViewHolder
package com.me.androidstudy2.adapter; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.me.androidstudy2.R; import com.me.androidstudy2.view.MyImageView; class MyViewHolder extends RecyclerView.ViewHolder { TextView title ; TextView content ; TextView date ; MyImageView img ; public MyViewHolder(@NonNull View itemView) { super(itemView); title = itemView.findViewById(R.id.tv_title); content = itemView.findViewById(R.id.tv_content); date = itemView.findViewById(R.id.tv_date); img = itemView.findViewById(R.id.iv_new); } }
2)NewAdapter
package com.me.androidstudy2.adapter; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.me.androidstudy2.R; import com.me.androidstudy2.domain.New; import java.util.ArrayList; import java.util.List; public class NewAdapter extends RecyclerView.Adapter<MyViewHolder> { List<New> data ; public List<New> getData() { return data; } public void setData(List<New> data) { this.data = data; } @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); View itemView = layoutInflater.inflate(R.layout.item_layout,parent,false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { holder.img.setImageURL(data.get(position).getImgsrc()); holder.content.setText(data.get(position).getDigest()); holder.title.setText(data.get(position).getTitle()); holder.date.setText(data.get(position).getPtime()); } @Override public int getItemCount() { return data.size(); } }
5、新闻实体:
1)New
package com.me.androidstudy2.domain; public class New { private String title; private String imgsrc; private String digest; private String ptime; public String getPtime() { return ptime; } public void setPtime(String ptime) { this.ptime = ptime; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getImgsrc() { return imgsrc; } public void setImgsrc(String imgsrc) { this.imgsrc = imgsrc; } public String getDigest() { return digest; } public void setDigest(String digest) { this.digest = digest; } }
2)News
package com.me.androidstudy2.domain; import java.util.List; public class News { private List<New> BBM54PGAwangning; private List<New> BA10TA81wangning; private List<New> BA8E6OEOwangning; private List<New> BA8EE5GMwangning; private List<New> BAI67OGGwangning; private List<New> BA8D4A3Rwangning; private List<New> BAI6I0O5wangning; private List<New> BAI6JOD9wangning; private List<New> BA8F6ICNwangning; private List<New> BAI6RHDKwangning; private List<New> BA8FF5PRwangning; private List<New> BDC4QSV3wangning; private List<New> BEO4GINLwangning; public List<New> getBA10TA81wangning() { return BA10TA81wangning; } public void setBA10TA81wangning(List<New> BA10TA81wangning) { this.BA10TA81wangning = BA10TA81wangning; } public List<New> getBA8E6OEOwangning() { return BA8E6OEOwangning; } public void setBA8E6OEOwangning(List<New> BA8E6OEOwangning) { this.BA8E6OEOwangning = BA8E6OEOwangning; } public List<New> getBA8EE5GMwangning() { return BA8EE5GMwangning; } public void setBA8EE5GMwangning(List<New> BA8EE5GMwangning) { this.BA8EE5GMwangning = BA8EE5GMwangning; } public List<New> getBAI67OGGwangning() { return BAI67OGGwangning; } public void setBAI67OGGwangning(List<New> BAI67OGGwangning) { this.BAI67OGGwangning = BAI67OGGwangning; } public List<New> getBA8D4A3Rwangning() { return BA8D4A3Rwangning; } public void setBA8D4A3Rwangning(List<New> BA8D4A3Rwangning) { this.BA8D4A3Rwangning = BA8D4A3Rwangning; } public List<New> getBAI6I0O5wangning() { return BAI6I0O5wangning; } public void setBAI6I0O5wangning(List<New> BAI6I0O5wangning) { this.BAI6I0O5wangning = BAI6I0O5wangning; } public List<New> getBAI6JOD9wangning() { return BAI6JOD9wangning; } public void setBAI6JOD9wangning(List<New> BAI6JOD9wangning) { this.BAI6JOD9wangning = BAI6JOD9wangning; } public List<New> getBA8F6ICNwangning() { return BA8F6ICNwangning; } public void setBA8F6ICNwangning(List<New> BA8F6ICNwangning) { this.BA8F6ICNwangning = BA8F6ICNwangning; } public List<New> getBAI6RHDKwangning() { return BAI6RHDKwangning; } public void setBAI6RHDKwangning(List<New> BAI6RHDKwangning) { this.BAI6RHDKwangning = BAI6RHDKwangning; } public List<New> getBA8FF5PRwangning() { return BA8FF5PRwangning; } public void setBA8FF5PRwangning(List<New> BA8FF5PRwangning) { this.BA8FF5PRwangning = BA8FF5PRwangning; } public List<New> getBDC4QSV3wangning() { return BDC4QSV3wangning; } public void setBDC4QSV3wangning(List<New> BDC4QSV3wangning) { this.BDC4QSV3wangning = BDC4QSV3wangning; } public List<New> getBEO4GINLwangning() { return BEO4GINLwangning; } public void setBEO4GINLwangning(List<New> BEO4GINLwangning) { this.BEO4GINLwangning = BEO4GINLwangning; } public List<New> getBBM54PGAwangning() { return BBM54PGAwangning; } public void setBBM54PGAwangning(List<New> BBM54PGAwangning) { this.BBM54PGAwangning = BBM54PGAwangning; } }
6、新闻项的布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <com.me.androidstudy2.view.MyImageView android:id="@+id/iv_new" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp"/> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:padding="4dp" android:orientation="vertical"> <TextView android:id="@+id/tv_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="24dp" android:maxLines="1" android:text="title"/> <TextView android:id="@+id/tv_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="13dp" android:maxLines="3" android:text="content"/> <TextView android:id="@+id/tv_date" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="right" android:textSize="10dp" android:layout_marginRight="5dp" android:text="content"/> </LinearLayout> </LinearLayout>
7、activity的布局:
<?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"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv_news" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
2、简单轮播图:
一、效果:手动轮播、自动轮播皆可。
二、设计思路:
1、自定义view,继承viewGroup类
2、求出子视图的个数,子视图高度,子视图宽度。假设轮播图的每一张图片大小相等根据第一个子视图的宽度和子视图的个数求出并设置viewGroup的宽高。
3、在onLayout方法中动态设置viewGroup的布局(绘制)。
4、事件设置:让onInterceptTouchEvent返回true,让onTouchEvent来处理事件,按下、移动、抬起需要做的事情。
5、使用Scroller来完成自动轮播。(当按下没有移动时停止自动轮播,抬起是重新开始轮播)。
6、通过一个接口实现单击事件和参数传递(用一个布尔变量表示是否是单击事件在按下时设置为true,移动时设置为false,在抬起时判断是否需要处理单击事件)。
三、代码
自定义的ViewGroup:
package com.me.androidstudy2.view; import android.annotation.SuppressLint; import android.content.Context; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.Scroller; import androidx.annotation.NonNull; import com.me.androidstudy2.jk.ImgViewGroupLister; import java.util.Timer; import java.util.TimerTask; public class ImgViewGroup extends ViewGroup { private int child; //子视图个数 private int childWidth; // 子视图宽 private int childHeight; //子视图高 private int x; private int index = 0; private Scroller scroller; private Timer timer = new Timer(); private TimerTask timerTask ; private boolean isAtuo = true; //默认开启自动轮播 private ImgViewGroupLister lister ; private boolean isClick; public ImgViewGroupLister getLister() { return lister; } public void setLister(ImgViewGroupLister lister) { this.lister = lister; } @SuppressLint("HandlerLeak") private Handler handler = new Handler(){ @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); switch (msg.what){ case 0: if (++index >= child ){ //到达最后一张图时,从初开始 index = 0; } scrollTo(childWidth * index,0); break; } } }; public ImgViewGroup(Context context) { super(context); InitObj(); } public ImgViewGroup(Context context, AttributeSet attrs) { super(context, attrs); InitObj(); } public ImgViewGroup(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); InitObj(); } public void InitObj(){ scroller = new Scroller(getContext()); timerTask = new TimerTask() { @Override public void run() { if(isAtuo){ handler.sendEmptyMessage(0); } } }; timer.schedule(timerTask,100,2000); } @Override public void computeScroll() { super.computeScroll(); if (scroller.computeScrollOffset()){ scrollTo(scroller.getCurrX(),0); invalidate(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //1、求出子视图的个数 child = getChildCount(); if (child == 0){ setMeasuredDimension(0,0); }else{ measureChildren(widthMeasureSpec,heightMeasureSpec); View view = getChildAt(0); childHeight = view.getMeasuredHeight(); childWidth = view.getMeasuredWidth(); int width = childWidth * child; int height = childHeight; //设置viewGroup的高度和宽度 setMeasuredDimension(width,height); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (changed){ int left = 0; for (int i = 0; i < child; i++) { View view = getChildAt(i); view.layout(left, 0, left+childWidth, childHeight); left += childWidth; } } } /** * 事件的传递 * 事件拦截 : */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return true; } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: //按下 isAtuo = false; if (!scroller.isFinished()){ scroller.abortAnimation(); } x = (int) event.getX(); isClick = true; break; case MotionEvent.ACTION_MOVE: //移动 int moveX = (int) event.getX(); int d = moveX - x; scrollBy(-d,0); x = moveX; isClick = false; break; case MotionEvent.ACTION_UP: //抬起 int scrollX = getScrollX(); index = (scrollX + childWidth / 2) /childWidth; //求下标,四舍五入 Log.e("xw",index + ""); if(index < 0){ //最左边 index = 0; }else if(index > child - 1){ //最右边 index = child - 1; } if (isClick){ lister.clickImg("http://u" + index + ".php"); }else{ // scrollTo(index * childWidth,0); int dx = index * childWidth -scrollX; scroller.startScroll(scrollX,0,dx,0); postInvalidate(); } isAtuo = true; break; default: break; } return true; } }
单击事件的接口:
package com.me.androidstudy2.jk; public interface ImgViewGroupLister { void clickImg(String url); }
active:
package com.me.androidstudy2; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.widget.ImageView; import android.widget.Toast; import com.me.androidstudy2.jk.ImgViewGroupLister; import com.me.androidstudy2.view.ImgViewGroup; public class MainActivity extends AppCompatActivity implements ImgViewGroupLister { private ImgViewGroup imgViewGroup ; public void initView(){ imgViewGroup = findViewById(R.id.img_view_group); } public void action(){ imgViewGroup.setLister(this); int [] ids = new int[] { R.drawable.xw7, R.drawable.xw4, R.drawable.xw2 }; for (int i = 0; i < ids.length; i++) { ImageView img = new ImageView(this); img.setBackgroundResource(ids[i]); imgViewGroup.addView(img); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); action(); } @Override public void clickImg(String url) { Toast.makeText(this,"url = " + url,Toast.LENGTH_SHORT).show(); } }