需要全部源码请点赞关注收藏后评论区留言~~~
一、需求描述
假定用户打开一个旅游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文章来源:https://www.toymoban.com/news/detail-475630.html
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>
创作不易 觉得有帮助请 点赞关注收藏~~~文章来源地址https://www.toymoban.com/news/detail-475630.html
到了这里,关于Android App开发实战项目之给用户推荐旅游信息图片(附源码 简单易懂)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!