Android中照片墙的经典实现

很多应用都使用到照片墙,今天来实现一个照片墙的界面,效果如如下:

本界面有如下优点:

1:可以无限加载照片,应用不崩溃

2:对于已经加载的照片使用缓存技术避免重复的网络数据加载

3:方便平时开发,比较经典

下面分几步来实现:

1:先定义主界面activity_main_album_wall.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:orientation="vertical">

    <!--我的相册头部标题栏-->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/title_bar_height"
        android:background="@drawable/bg_color_white">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="我的相册"
            android:textColor="@color/titleBlack"
            android:textSize="18sp" />

        <ImageView
            android:id="@+id/my_album_wall_back"
            android:layout_width="wrap_content"
            android:layout_height="@dimen/title_bar_height"
            android:layout_alignParentStart="true"
            android:layout_centerInParent="true"
            android:contentDescription="@string/word_back"
            android:onClick="onClick"
            android:paddingStart="16dp"
            android:src="@drawable/nav_icon_return_black" />

        <TextView
            android:id="@+id/edit_image"
            android:layout_width="wrap_content"
            android:layout_height="@dimen/title_bar_height"
            android:layout_alignParentEnd="true"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:paddingEnd="16dp"
            android:text="编辑"
            android:textColor="@color/textColor"
            android:textSize="15sp" />
    </RelativeLayout>

    <!--之前的只是显示图片-->
    <!--<GridView-->
        <!--android:id="@+id/grid_view_image"-->
        <!--android:layout_width="match_parent"-->
        <!--android:layout_height="wrap_content"-->
        <!--android:layout_marginTop="10dp"-->
        <!--android:numColumns="4">-->
    <!--</GridView>-->

    <android.support.v7.widget.RecyclerView
        android:id="@+id/album_wall_recycler"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </android.support.v7.widget.RecyclerView>
</LinearLayout>

 

2:定义主界面中的RecyclerView的子界面:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/white"
    android:orientation="vertical">

    <TextView
        android:id="@+id/image_date"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="2018年6月12日"
        android:textColor="@color/textColor"
        android:textSize="16sp"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="10dp"/>

    <com.choicelean.superwinner.component.MyAlbumWallGridView
        android:id="@+id/grid_view_image"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:numColumns="4"
        android:layout_marginTop="10dp">
    </com.choicelean.superwinner.component.MyAlbumWallGridView>
</LinearLayout>

3:定义GridView的子界面:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/wall_iamge_item"
        android:layout_width="88dp"
        android:layout_height="88dp" />
</LinearLayout>

4:OK,界面定义好了之后开始编写我们的Adapter了,这也是最重要的一步步骤写的很详细:

/**
 * 照片墙的适配器类,这里对照片墙中的照片进行了优化,利用LRUCache类进行优化我们的照片
 * 在调用的时候注意这个类
 * Created by acer-pc on 2018/8/8.
 */

public class PhotoWallAdapter extends ArrayAdapter<String> implements AbsListView.OnScrollListener {
    /**
     * 记录所有正在下载或等待下载的任务。
     */
    private Set<BitmapWorkerTask> taskCollection;

    /**
     * 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。
     */
    private LruCache<String, Bitmap> mMemoryCache;

    /**
     * GridView的实例
     */
    private GridView mPhotoWall;

    /**
     * 第一张可见图片的下标
     */
    private int mFirstVisibleItem;

    /**
     * 一屏有多少张图片可见
     */
    private int mVisibleItemCount;

    /**
     * 记录是否刚打开程序,用于解决进入程序不滚动屏幕,不会下载图片的问题。
     */
    private boolean isFirstEnter = true;

    /**
     * 把图片路径的集合传递进来
     *
     * @param context
     * @param textViewResourceId
     * @param objects
     * @param photoWall
     */
    private String[] mImageLists;


    public PhotoWallAdapter(Context context, int textViewResourceId, String[] objects,
                            GridView photoWall) {
        super(context, textViewResourceId, objects);
        mImageLists = objects;
        mPhotoWall = photoWall;
        taskCollection = new HashSet<BitmapWorkerTask>();
        // 获取应用程序最大可用内存
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheSize = maxMemory / 8;
        // 设置图片缓存大小为程序最大可用内存的1/8
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount();
            }
        };
        mPhotoWall.setOnScrollListener(this);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final String url = getItem(position);
        View view;
        if (convertView == null) {
            //子项布局
            view = LayoutInflater.from(getContext()).inflate(R.layout.activity_mine_album_wall_item_item, null);
        } else {
            view = convertView;
        }
        final ImageView photo = (ImageView) view.findViewById(R.id.wall_iamge_item);
        // 给ImageView设置一个Tag,保证异步加载图片时不会乱序
        photo.setTag(url);
        setImageView(url, photo);
        return view;
    }

    /**
     * 给ImageView设置图片。首先从LruCache中取出图片的缓存,设置到ImageView上。如果LruCache中没有该图片的缓存,
     * 就给ImageView设置一张默认图片。
     *
     * @param imageUrl  图片的URL地址,用于作为LruCache的键。
     * @param imageView 用于显示图片的控件。
     */
    private void setImageView(String imageUrl, ImageView imageView) {
        Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
        } else {
            //图片为空的时候,显示一张空的图片资源
            imageView.setImageResource(R.drawable.load_err_empty);
        }
    }

    /**
     * 将一张图片存储到LruCache中。
     *
     * @param key    LruCache的键,这里传入图片的URL地址。
     * @param bitmap LruCache的键,这里传入从网络上下载的Bitmap对象。
     */
    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemoryCache(key) == null) {
            mMemoryCache.put(key, bitmap);
        }
    }

    /**
     * 从LruCache中获取一张图片,如果不存在就返回null。
     *
     * @param key LruCache的键,这里传入图片的URL地址。
     * @return 对应传入键的Bitmap对象,或者null。
     */
    public Bitmap getBitmapFromMemoryCache(String key) {
        return mMemoryCache.get(key);
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // 仅当GridView静止时才去下载图片,GridView滑动时取消所有正在下载的任务
        if (scrollState == SCROLL_STATE_IDLE) {
            loadBitmaps(mFirstVisibleItem, mVisibleItemCount);
        } else {
            cancelAllTasks();
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
                         int totalItemCount) {
        mFirstVisibleItem = firstVisibleItem;
        mVisibleItemCount = visibleItemCount;
        // 下载的任务应该由onScrollStateChanged里调用,但首次进入程序时onScrollStateChanged并不会调用,
        // 因此在这里为首次进入程序开启下载任务。
        if (isFirstEnter && visibleItemCount > 0) {
            loadBitmaps(firstVisibleItem, visibleItemCount);
            isFirstEnter = false;
        }
    }

    /**
     * 加载Bitmap对象。此方法会在LruCache中检查所有屏幕中可见的ImageView的Bitmap对象,
     * 如果发现任何一个ImageView的Bitmap对象不在缓存中,就会开启异步线程去下载图片。
     *
     * @param firstVisibleItem 第一个可见的ImageView的下标
     * @param visibleItemCount 屏幕中总共可见的元素数
     */
    private void loadBitmaps(int firstVisibleItem, int visibleItemCount) {
        try {
            for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) {
                /**
                 *    图片的来源,这里本应该是图片的地址资源集合;
                 */
                //String imageUrl = Images.imageThumbUrls[i];
                String imageUrl = mImageLists[i];
                Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);
                if (bitmap == null) {
                    BitmapWorkerTask task = new BitmapWorkerTask();
                    taskCollection.add(task);
                    task.execute(imageUrl);
                } else {
                    ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);
                    if (imageView != null && bitmap != null) {
                        imageView.setImageBitmap(bitmap);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 取消所有正在下载或等待下载的任务。
     */
    public void cancelAllTasks() {
        if (taskCollection != null) {
            for (BitmapWorkerTask task : taskCollection) {
                task.cancel(false);
            }
        }
    }

    /**
     * 异步下载图片的任务。
     *
     * @author guolin
     */
    class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {

        /**
         * 图片的URL地址
         */
        private String imageUrl;

        @Override
        protected Bitmap doInBackground(String... params) {
            imageUrl = params[0];
            // 在后台开始下载图片
            final Bitmap bitmap = downloadBitmap(params[0]);

            if (bitmap != null) {
                // 图片下载完成后缓存到LrcCache中
                addBitmapToMemoryCache(params[0], bitmap);
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            // 根据Tag找到相应的ImageView控件,将下载好的图片显示出来。
            ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);
            if (imageView != null && bitmap != null) {
                imageView.setImageBitmap(bitmap);
            }
            taskCollection.remove(this);
        }

        /**
         * 建立HTTP请求,并获取Bitmap对象。
         *
         * @param imageUrl 图片的URL地址
         * @return 解析后的Bitmap对象
         */
        private Bitmap downloadBitmap(String imageUrl) {
            Bitmap bitmap = null;
            HttpURLConnection con = null;
            try {
                URL url = new URL(imageUrl);
                con = (HttpURLConnection) url.openConnection();
                con.setConnectTimeout(10 * 1000);
                con.setReadTimeout(10 * 1000);
                bitmap = BitmapFactory.decodeStream(con.getInputStream());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (con != null) {
                    con.disconnect();
                }
            }
            return bitmap;
        }

        /**
         * 自己写的图片URL转换bitmap对象
         */
        public Bitmap returnBitMap(final String url) {
            Bitmap bitmap = null;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    URL imageurl = null;
                    Bitmap bitmaps;
                    try {
                        imageurl = new URL(url);
                    } catch (MalformedURLException e) {
                        e.printStackTrace();
                    }
                    try {
                        HttpURLConnection conn = (HttpURLConnection) imageurl.openConnection();
                        conn.setDoInput(true);
                        conn.connect();
                        InputStream is = conn.getInputStream();
                        bitmaps = BitmapFactory.decodeStream(is);
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();

            return bitmap;
        }

    }
}

 

5:最后写我们的主界面:

/**
 * 相册墙,展示照片
 */
public class MineAlbumWallActivity extends AppCompatActivity implements View.OnClickListener {

    private ImageView albumWallBack;
    private TextView editText;

    //照片墙是放在RecyclerView中来显示的
    private RecyclerView albumRecyclerView;

    //获取调用的类型根据类型来显示照片
    private int type;

    //照片墙的适配类,定义成全局,这样使用可以在推出当前界面的时候取消任务的下载
    // private PhotoWallAdapter adapter;

    //获取相册的详细照片
    private List<AlbumWallRecent> albumList;
    private List<ImageList> imageLists;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mine_album_wall);
        initView();
        initData();
    }

    public void initView() {
        albumWallBack = (ImageView) findViewById(R.id.my_album_wall_back);
        editText = (TextView) findViewById(R.id.edit_image);
        albumRecyclerView = findViewById(R.id.album_wall_recycler);
        albumWallBack.setOnClickListener(this);
        editText.setOnClickListener(this);

        LinearLayoutManager layoutManager = new LinearLayoutManager(MineAlbumWallActivity.this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        albumRecyclerView.setLayoutManager(layoutManager);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.my_album_wall_back:
                finish();
                break;
            case R.id.edit_image:
                Toast.makeText(this, "编辑照片", Toast.LENGTH_SHORT).show();
                break;
            default:
                break;
        }
    }

    public void initData() {
        type = getIntent().getIntExtra("type", 0);
        //从这里开始加载请求网络数据
        final String myToken = AndroidFileUtil.readFileByLines(getCacheDir().getAbsolutePath() + "/" + DataConfig.TOKEN_FILE_NAME);
        APIConfig.getDataIntoView(new Runnable() {
            @Override
            public void run() {
                Map<String, String> map = new HashMap<>();
                map.put("token", myToken);
                map.put("type", String.valueOf(type));
                String paramJson = EncryptUtil.encrypt(map);
                //String url = "http://47.104.67.3:8080/superwinner/shop/static/api/diary/galleryDetail.do";
                String url = APIConfig.getORIGINALAPIUrl(APIConfig.API_DIARY_GALLERYDETAIL);
                String rs = HttpUtil.GetDataFromNetByPost(url,
                        new ParamsBuilder().addParam("paramJson", paramJson).getParams());
                // rs判空
                final MineAlbumWallResult result = EncryptUtil.decrypt(rs, MineAlbumWallResult.class);
                UIUtils.runOnUIThread(new Runnable() {
                    @Override
                    public void run() {
                        //这里禁用
                        if (result != null && result.getResult() == APIConfig.CODE_SUCCESS) {
                            /**
                             * 获取单个日期List
                             */
                            albumList = result.getData().getAlbumList();
                            AlbumWallAdapter albumWallAdapter = new AlbumWallAdapter(albumList);
                            albumRecyclerView.setAdapter(albumWallAdapter);
                        } else {
                            ToastUtil.toastByCode(result);
                        }
                    }
                });
            }
        });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 退出程序时结束所有的下载任务
        // adapter.cancelAllTasks();
    }


    /**
     * **************************************************************************************
     * 相册中的照片放在RecyclerView中来显示
     * 一个item只显示一天的相册
     */
    private class AlbumWallAdapter extends RecyclerView.Adapter<AlbumWallAdapter.ViewHolder> {
        private List<AlbumWallRecent> mAlbumDayList;

        class ViewHolder extends RecyclerView.ViewHolder {
            TextView imageDate;
            GridView gridViewImage;

            public ViewHolder(View itemView) {
                super(itemView);
                imageDate = (TextView) itemView.findViewById(R.id.image_date);
                gridViewImage = (GridView) itemView.findViewById(R.id.grid_view_image);
            }
        }

        public AlbumWallAdapter(List<AlbumWallRecent> albumDays) {
            mAlbumDayList = albumDays;
        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_mine_album_wall_item, parent, false);
            ViewHolder holder = new ViewHolder(view);
            return holder;
        }

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            AlbumWallRecent albumDay = mAlbumDayList.get(position);
            holder.imageDate.setText(albumDay.getDate());

            //图片存放的集合类把照片URL显示出来,最后放到数组里面再在adapter里面加载
            //局部变量
            String imagesUrl = "";
            String[] imageThumbUrls;
            /**
             * 下面对GridView进行加载
             */
            imageLists = albumDay.getImageList();
            /**
             * 这里把返回的图片URL地址集合拼接成字符串数组
             */
            for (int i = 0; i < imageLists.size(); i++) {
                //保存url
                String diaryImageUrl = imageLists.get(i).getImage();
                if ("".equals(imagesUrl)) {
                    imagesUrl = diaryImageUrl;
                } else {
                    imagesUrl = imagesUrl + "," + diaryImageUrl;
                }
            }
            /**
             * 再将字符串转换为字符串数组
             */
            imageThumbUrls = imagesUrl.split(",");
            PhotoWallAdapter adapter = new PhotoWallAdapter(MineAlbumWallActivity.this, 0, imageThumbUrls, holder.gridViewImage);
            holder.gridViewImage.setAdapter(adapter);
        }

        @Override
        public int getItemCount() {
            return mAlbumDayList.size();
        }
    }
}

 

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
GridView Android 常用的布局控件之一,可以用来实现照片墙的效果。下面是一个简单的实现步骤: 1. 在布局文件添加 GridView 控件: ``` <GridView android:id="@+id/grid_view" android:numColumns="3" android:verticalSpacing="10dp" android:horizontalSpacing="10dp" android:stretchMode="columnWidth" android:padding="10dp" android:layout_width="match_parent" android:layout_height="wrap_content" /> ``` 其,numColumns 表示每行显示几个图片,verticalSpacing 和 horizontalSpacing 表示图片之间的垂直和水平间距,stretchMode 表示如何拉伸图片以填充空白区域,padding 表示 GridView 的内边距。 2. 创建一个 ImageAdapter 类,继承自 BaseAdapter,并实现 getView() 方法。在 getView() 方法,可以加载图片并显示在 ImageView 控件: ``` public class ImageAdapter extends BaseAdapter { private Context mContext; private int[] mImageIds = {R.drawable.image1, R.drawable.image2, R.drawable.image3, ...}; public ImageAdapter(Context c) { mContext = c; } public int getCount() { return mImageIds.length; } public Object getItem(int position) { return null; } public long getItemId(int position) { return 0; } public View getView(int position, View convertView, ViewGroup parent) { ImageView imageView; if (convertView == null) { imageView = new ImageView(mContext); imageView.setLayoutParams(new GridView.LayoutParams(350, 350)); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); } else { imageView = (ImageView) convertView; } imageView.setImageResource(mImageIds[position]); return imageView; } } ``` 3. 在 Activity 获取 GridView 控件,并设置 ImageAdapter 为其适配器: ``` GridView gridView = (GridView) findViewById(R.id.grid_view); ImageAdapter adapter = new ImageAdapter(this); gridView.setAdapter(adapter); ``` 这样就可以实现一个简单的照片墙了。当然,如果需要更复杂的功能,还可以继续优化代码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值