需要全部源码请点赞关注收藏后评论区留言~~~
一、需求描述
假定用户打开一个旅游App想看看哪里风景比较优美,那么App应当展示各地的风景名声图片,为了让界面不太呆板,可以考虑交错显示风景图片,接着用户向下拉动页面,想要刷新界面浏览更多的图片,此时App界面响应下拉刷新手势弹出加载源泉
等待App努力加载新的图片列表,加载完成之后,界面展示新一批的风景图片,同时加载圆圈消失。
接下来我们实践如何让App从服务端获取随机推荐的风景图片
二、界面设计
界面设计比较简单 主要用到了以下控件
1:循环视图RecyclerView的瀑布流布局
2:下拉刷新布局 SwipeRefreshLayout
界面简单,但是背后设计的网络技术比较复杂 主要用到了以下及几种技术
1:HTTP接口调用 App向后端服务器请求风景图片列表
2:JSON格式 App与服务器之间的数据交互
3:异步任务AsyncTask 访问HTTP接口耗时,需要放在专门的异步任务之中
4:图片加载框架Glide 加载网络图片并显示在界面上
效果如下 此处建议连接真机测试 模拟机不好与后端网络交互
三、关键部分
1:循环视图的首次加载与重新加载
2:原始网络图片的加载
3:不同部分源码之间关系
1:GuessLikeActivity 风景列表的活动代码 主类
2:PhotoRecyclerAdapter 风景图片的适配器代码
3:PhotoDetailActivity 图片详情的活动代码
4:GetPhotoTask 获取网络图片的任务代码 通过调用HTTP接口,从后端服务器获得JSON格式的风景图片信息列表
5:服务端工程的GetPhoto 图片获取接口的服务端代码
四、代码
GuessLikeActivity
package com.example.chapter14; import android.annotation.SuppressLint; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.StaggeredGridLayoutManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener; import com.example.chapter14.adapter.PhotoRecyclerAdapter; import com.example.chapter14.bean.PhotoInfo; import com.example.chapter14.task.resp.GetPhotoResp; import com.example.chapter14.task.GetPhotoTask; import com.example.chapter14.widget.SpacesDecoration; import com.google.gson.Gson; import java.util.List; @SuppressLint("DefaultLocale") public class GuessLikeActivity extends AppCompatActivity implements View.OnClickListener, OnRefreshListener, GetPhotoTask.GetPhotoListener { private final static String TAG = "GuessLikeActivity"; private SwipeRefreshLayout srl_like; // 声明一个下拉刷新布局对象 private RecyclerView rv_like; // 声明一个循环视图对象 private PhotoRecyclerAdapter mAdapter; // 声明一个线性适配器对象 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_guess_like); TextView tv_title = findViewById(R.id.tv_title); tv_title.setText("大美河山"); findViewById(R.id.iv_back).setOnClickListener(this); initRecyclerView(); // 初始化瀑布流布局的循环视图 } @Override public void onClick(View v) { if (v.getId() == R.id.iv_back) { finish(); // 关闭当前页面 } } // 初始化瀑布流布局的循环视图 private void initRecyclerView() { rv_like = findViewById(R.id.rv_like); // 从布局文件中获取名叫rv_like的循环视图 // 创建一个垂直方向的瀑布流网格布局管理器 StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(2, RecyclerView.VERTICAL); rv_like.setLayoutManager(manager); // 设置循环视图的布局管理器 rv_like.addItemDecoration(new SpacesDecoration(1)); // 设置循环视图的空白装饰 srl_like = findViewById(R.id.srl_like); // 从布局文件中获取名叫srl_like的下拉刷新布局 srl_like.setOnRefreshListener(this); // 设置下拉刷新布局的下拉刷新监听器 // 设置下拉刷新布局的进度圆圈颜色 srl_like.setColorSchemeResources(R.color.red, R.color.orange, R.color.green, R.color.blue); srl_like.setRefreshing(true); // 设置状态为正在刷新,此时会弹出进度圆圈 onRefresh(); // 执行刷新动作 } // 一旦在下拉刷新布局内部往下拉动页面,就触发下拉监听器的onRefresh方法 @Override public void onRefresh() { GetPhotoTask task = new GetPhotoTask(); // 创建一个获取照片的异步任务 task.setGetPhotoListener(this); // 设置照片获取的监听器 task.execute(); // 把照片获取任务加入到处理队列 } // 在获得照片列表信息后触发 @Override public void onGetPhoto(String resp) { srl_like.setRefreshing(false); // 设置状态为正在刷新,此时会关闭进度圆圈 // 把JSON串转换为对应结构的实体对象 GetPhotoResp photoResp = new Gson().fromJson(resp, GetPhotoResp.class); if (photoResp == null) { // 未获得返回报文,说明HTTP调用失败 return; } List<PhotoInfo> photo_list = photoResp.getPhotoList(); if (photo_list!=null && photo_list.size()>0) { Log.d(TAG, "photo_list.size()="+photo_list.size()); if (mAdapter == null) { // 首次加载前不存在适配器 // 构建一个照片列表的瀑布流网格适配器 mAdapter = new PhotoRecyclerAdapter(this, photo_list); mAdapter.setOnItemClickListener(mAdapter); // 设置照片列表的点击监听器 rv_like.setAdapter(mAdapter); // 设置循环视图的瀑布流网格适配器 } else { // 再次加载时已经存在适配器了 mAdapter.setPhotoList(photo_list); mAdapter.notifyDataSetChanged(); // 通知适配器发生了数据变更 } rv_like.scrollToPosition(0); // 让循环视图滚动到第一项所在的位置 } } }
PhotoRecyclerAdapter
package com.example.chapter14.adapter; import android.content.Context; import android.content.Intent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.ViewHolder; import com.bumptech.glide.Glide; import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; import com.example.chapter14.PhotoDetailActivity; import com.example.chapter14.R; import com.example.chapter14.bean.PhotoInfo; import com.example.chapter14.util.Utils; import com.example.chapter14.widget.RecyclerExtras.OnItemClickListener; import java.util.List; import java.util.Random; public class PhotoRecyclerAdapter extends RecyclerView.Adapter<ViewHolder> implements OnItemClickListener { private final static String TAG = "PhotoRecyclerAdapter"; private Context mContext; // 声明一个上下文对象 private List<PhotoInfo> mPhotoList; // 照片列表 public PhotoRecyclerAdapter(Context context, List<PhotoInfo> photoList) { mContext = context; mPhotoList = photoList; } public void setPhotoList(List<PhotoInfo> photoList) { mPhotoList = photoList; } // 获取列表项的个数 public int getItemCount() { return mPhotoList.size(); } // 创建列表项的视图持有者 public ViewHolder onCreateViewHolder(ViewGroup vg, int viewType) { // 根据布局文件item_photo.xml生成视图对象 View v = LayoutInflater.from(mContext).inflate(R.layout.item_photo, vg, false); return new ItemHolder(v); } // 绑定列表项的视图持有者 public void onBindViewHolder(ViewHolder vh, final int position) { ItemHolder holder = (ItemHolder) vh; PhotoInfo photo = mPhotoList.get(position); ViewGroup.LayoutParams params = holder.ll_item.getLayoutParams(); params.height = 150 + new Random().nextInt(100); // 生成随机高度,从而呈现瀑布流效果 params.height = Utils.dip2px(mContext, params.height); holder.ll_item.setLayoutParams(params); holder.tv_title.setText(photo.title); // 利用Glide加载网络图片,并在图像视图上显示 Glide.with(mContext).load(photo.image_url) .transition(DrawableTransitionOptions.withCrossFade(1000)) // 设置时长1秒的渐变动画 .into(holder.iv_pic); // 列表项的点击事件需要自己实现 holder.ll_item.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mOnItemClickListener != null) { mOnItemClickListener.onItemClick(v, position); } } }); } // 获取列表项的类型 public int getItemViewType(int position) { return 0; } // 获取列表项的编号 public long getItemId(int position) { return position; } // 定义列表项的视图持有者 public class ItemHolder extends ViewHolder { public LinearLayout ll_item; // 声明列表项的线性布局 public ImageView iv_pic; // 声明一个照片的图像视图 public TextView tv_title; // 声明一个标题的文本视图 public ItemHolder(View v) { super(v); ll_item = v.findViewById(R.id.ll_item); iv_pic = v.findViewById(R.id.iv_pic); tv_title = v.findViewById(R.id.tv_title); } } // 声明列表项的点击监听器对象 private OnItemClickListener mOnItemClickListener; public void setOnItemClickListener(OnItemClickListener listener) { this.mOnItemClickListener = listener; } // 处理列表项的点击事件 public void onItemClick(View view, int position) { PhotoInfo photo = mPhotoList.get(position); // 以下跳到照片详情页面 Intent intent = new Intent(mContext, PhotoDetailActivity.class); intent.putExtra("title", photo.title); intent.putExtra("image_url", photo.image_url); mContext.startActivity(intent); // 打开照片详情页面 } }
PhotoDetailActivity
package com.example.chapter14; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import com.bumptech.glide.Glide; import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.Target; public class PhotoDetailActivity extends AppCompatActivity implements View.OnClickListener { private final static String TAG = "PhotoDetailActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_photo_detail); String title = getIntent().getStringExtra("title"); String image_url = getIntent().getStringExtra("image_url"); TextView tv_title = findViewById(R.id.tv_title); tv_title.setText(title); findViewById(R.id.iv_back).setOnClickListener(this); ImageView iv_photo = findViewById(R.id.iv_photo); // 构建一个加载网络图片的建造器 RequestBuilder<Drawable> builder = Glide.with(this).load(image_url) .transition(DrawableTransitionOptions.withCrossFade(1000)); // 设置时长1秒的渐变动画 RequestOptions options = new RequestOptions(); // 创建Glide的请求选项 options.override(Target.SIZE_ORIGINAL); // 展示原始图片 options.disallowHardwareConfig(); // 关闭硬件加速,防止过大尺寸的图片加载报错 // 在图像视图上展示网络图片。apply方法表示启用指定的请求选项 builder.apply(options).into(iv_photo); } @Override public void onClick(View v) { if (v.getId() == R.id.iv_back) { finish(); // 关闭当前页面 } } }
XML文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <include layout="@layout/title_tour" /> <androidx.swiperefreshlayout.widget.SwipeRefreshLayout android:id="@+id/srl_like" android:layout_width="match_parent" android:layout_height="wrap_content"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv_like" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#eeeeee" /> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout> </LinearLayout>
创作不易 觉得有帮助请 点赞关注收藏~~~