实现一个简易的图片画廊
数据类:
public class Pixabay {
private int totalHits;
private int total;
private ArrayList<PhotoItem> hits=new ArrayList<>();
public int getTotalHits() {
return totalHits;
}
public void setTotalHits(int totalHits) {
this.totalHits = totalHits;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public ArrayList<PhotoItem> getHits() {
return hits;
}
public void setHits(ArrayList<PhotoItem> hits) {
this.hits = hits;
}
}
class PhotoItem{
@SerializedName("webformatURL")
private String preViewURL;
@SerializedName("largeImageURL")
private String fullURL;
@SerializedName("id")
private int photoId;
public String getPreViewURL() {
return preViewURL;
}
public void setPreViewURL(String preViewURL) {
this.preViewURL = preViewURL;
}
public String getFullURL() {
return fullURL;
}
public void setFullURL(String fullURL) {
this.fullURL = fullURL;
}
public int getPhotoId() {
return photoId;
}
public void setPhotoId(int photoId) {
this.photoId = photoId;
}
}
用单例的方式获取Volley:
public class VolleySingleton {
private static VolleySingleton volleySingleton=null;
private static RequestQueue requestQueue=null;
private Context mContext;
public static synchronized VolleySingleton getInstance(Context context){
if (volleySingleton==null){
volleySingleton=new VolleySingleton(context);
}
return volleySingleton;
}
private VolleySingleton(Context context){
mContext=context;
requestQueue= getRequestQueue();
}
public RequestQueue getRequestQueue(){
if (requestQueue==null){
requestQueue= Volley.newRequestQueue(mContext);
}
return requestQueue;
}
}
构造一个ViewModel以存放获取到的图片的信息:
public class GalleryViewModel extends AndroidViewModel {
private MutableLiveData<List<PhotoItem>> photoListLive= new MutableLiveData<>();
private ArrayList<String> keyWords=new ArrayList<>();
public GalleryViewModel(@NonNull Application application) {
super(application);
keyWords.add("cat");
keyWords.add("dog");
keyWords.add("car");
keyWords.add("beauty");
keyWords.add("phone");
keyWords.add("computer");
keyWords.add("flower");
keyWords.add("animal");
}
public void fetchData(){
StringRequest stringRequest=new StringRequest
(Request.Method.GET,
getUrl(),
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Gson gson=new Gson();
Pixabay pixabay=gson.fromJson(response,Pixabay.class);
photoListLive.setValue(pixabay.getHits());
Log.i("GalleryViewModel",pixabay.getHits().toString());
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.i("GalleryViewModel",error.toString());
}
});
VolleySingleton.getInstance(getApplication()).getRequestQueue().add(stringRequest);
}
private String getUrl(){
return "https://pixabay.com/api/?key=18976664-784daca1700270b238f252820&q="+keyWords.get(new Random().nextInt(keyWords.size()));
}
public MutableLiveData<List<PhotoItem>> getPhotoListLive() {
return photoListLive;
}
}
写一个RecyclerView的adapter
public class GalleryAdapter extends ListAdapter<PhotoItem, MyViewHolder> {
protected GalleryAdapter() {
super(new DiffUtil.ItemCallback<PhotoItem>() {
@Override
public boolean areItemsTheSame(@NonNull PhotoItem oldItem, @NonNull PhotoItem newItem) {
return oldItem==newItem;
}
@Override
public boolean areContentsTheSame(@NonNull PhotoItem oldItem, @NonNull PhotoItem newItem) {
return oldItem.getPhotoId()==newItem.getPhotoId();
}
});
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view=LayoutInflater.from(parent.getContext()).inflate(R.layout.gallery_cell,parent,false);
MyViewHolder holder=new MyViewHolder(view);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
return holder;
}
@Override
public void onBindViewHolder(@NonNull final MyViewHolder holder, int position) {
ImageView imageView=holder.itemView.findViewById(R.id.imageView);
final ShimmerLayout shimmerLayout=holder.itemView.findViewById(R.id.shimmerLayoutCell);
shimmerLayout.setShimmerColor(0x55ffffff);//55表示透明度
shimmerLayout.setShimmerAngle(0);
shimmerLayout.startShimmerAnimation();
Glide.with(holder.itemView)//view
.load(getItem(position).getPreViewURL())
.placeholder(R.drawable.ic_baseline_photo_24)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
shimmerLayout.stopShimmerAnimation();
return false;
}
})
.into(imageView);
}
}
class MyViewHolder extends RecyclerView.ViewHolder{
public MyViewHolder(@NonNull View itemView) {
super(itemView);
}
}
在GalleryFragment中显示界面:
public class GalleryFragment extends Fragment {
GalleryViewModel galleryViewModel;
GalleryAdapter galleryAdapter;
RecyclerView recyclerView;
public GalleryFragment() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
galleryViewModel=new ViewModelProvider(this).get(GalleryViewModel.class);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view=inflater.inflate(R.layout.fragment_gallery, container, false);
recyclerView=view.findViewById(R.id.recyclerView);
galleryAdapter=new GalleryAdapter();
recyclerView.setAdapter(galleryAdapter);
recyclerView.setLayoutManager(new GridLayoutManager(requireContext(),2));
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
galleryViewModel.getPhotoListLive().observe(getViewLifecycleOwner(), new Observer<List<PhotoItem>>() {
@Override
public void onChanged(List<PhotoItem> photoItems) {
galleryAdapter.submitList(photoItems);
}
});
if (galleryViewModel.getPhotoListLive().getValue()==null){//一定要放在观察者下面
galleryViewModel.fetchData();
}
}
galleryViewModel.getPhotoListLive().observe(getViewLifecycleOwner(), new Observer<List<PhotoItem>>() {
@Override
public void onChanged(List<PhotoItem> photoItems) {
galleryAdapter.submitList(photoItems);
}
});
}
}
有一个我非常疑惑的点:如果我在viewModel中一开始就初始化了photoList(即不判断是否为空,而是判断size是否为0),当且仅当第一次打开app时会加载图片。
至此为止,已经可以初步的完成了最基础的功能,即打开app之后就能加载图片:
实现下拉刷新,并在刷新后取消动画:
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
galleryViewModel.getPhotoListLive().observe(getViewLifecycleOwner(), new Observer<List<PhotoItem>>() {
@Override
public void onChanged(List<PhotoItem> photoItems) {
galleryAdapter.submitList(photoItems);
swipeRefreshLayout.setRefreshing(false);
//取消动画
}
});
//...
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
galleryViewModel.fetchData();
}
});
}
添加一个菜单栏来实现刷新功能,同样添加刷新动画,并且延长刷新动画时间:
//GalleryFragment
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu,menu);
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.swipeIndicator:
swipeRefreshLayout.setRefreshing(true);
//添加刷新动画
galleryViewModel.fetchData();
}
return super.onOptionsItemSelected(item);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
galleryViewModel.getPhotoListLive().observe(getViewLifecycleOwner(), new Observer<List<PhotoItem>>() {
@Override
public void onChanged(List<PhotoItem> photoItems) {
galleryAdapter.submitList(photoItems);
Handler handler=new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
swipeRefreshLayout.setRefreshing(false);
}
},1000);//使得加载完成后将取消动画的动作推迟1s,即这个动画能多做1s
}
});
//...
}
到此,这个加载、刷新图片的页面就可以完成了。接下来将会去写第二个页面,即查看大图。
首先要先将Adapter中留白的点击监听器补全,并将这个photoItem传入:
//GalleryAdapter
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view=LayoutInflater.from(parent.getContext()).inflate(R.layout.gallery_cell,parent,false);
final MyViewHolder holder=new MyViewHolder(view);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Bundle bundle=new Bundle();
bundle.putParcelable("PHOTO", getItem(holder.getAdapterPosition()));
NavController navController = Navigation.findNavController(holder.itemView);
navController.navigate(R.id.action_galleryFragment_to_photoFragment, bundle);
}
});
return holder;
}
其中需要先将PhotoItem这个类序列化:
class PhotoItem implements Parcelable{
}
在PhotoFragment中接受PhotoItem,并加载图片:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view=inflater.inflate(R.layout.fragment_photo, container, false);
shimmerLayout=view.findViewById(R.id.shimmerLayoutPhoto);
shimmerLayout.setShimmerColor(0x55ffffff);//55表示透明度
shimmerLayout.setShimmerAngle(0);
shimmerLayout.startShimmerAnimation();
photoView =view.findViewById(R.id.photoView);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
PhotoItem photoItem= getArguments() != null ? (PhotoItem) getArguments().getParcelable("PHOTO") : null;
Glide.with(requireContext())
.load(photoItem != null ? photoItem.getFullURL() : null)
.placeholder(R.drawable.ic_baseline_photo_24)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
shimmerLayout.stopShimmerAnimation();
return false;
}
})
.into(photoView);
}
设置返回键:
//MainActivity
public class MainActivity extends AppCompatActivity {
private NavController navController;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
navController = Navigation.findNavController(this,R.id.fragment);
NavigationUI.setupActionBarWithNavController(this,navController);
}
@Override
public boolean onSupportNavigateUp() {
return super.onSupportNavigateUp()||navController.navigateUp();
}
}
最终效果: