বাংলায় অ্যান্ড্রয়েড সহায়িকা - Bangla Android Guide

একটি গ্রিডভিউ বাস্তবায়নে বিটম্যাপ লোড করা

grid list building block ইমেজ ডাটা সেট দেখানোর জন্য উপকারী এবং একটি GridView কম্পোনেন্ট ব্যবহার করে বাস্তবায়িত হতে পারে যার মধ্যে অনেক ইমেজ যে কোন সময় এক সাথে অন-স্ক্রিন হতে পারে এবং আরও অনেকগুলো দৃশ্যমান হতে প্রস্তুত হওয়া দরকার যদি ইউজার আপ বা ডাউনে স্ক্রল করে। যখন এই ধরনের নিয়ন্ত্রণ বাস্তবায়ন করা হয়, আপনাকে নিশ্চিত করতে হবে যে ইউআই ফ্লুইড ধরে রাখে, মেমরী ব্যবহার নিয়ন্ত্রণে রাখে এবং কনকারেন্সি সঠিকভাবে ব্যাবস্থপাপন করা হয় (GridView চিলড্রেন ভিউ রিসাইকেল করার কারনে)।

শুরুতে, ImageView চিলড্রেন সহকারে একটি আদর্শ GridView বাস্তবায়ন একটি Fragment এর মধ্যে স্থাপন করে। পূণরায়, এটাকে মনে হতে পারে একটি সম্পূর্ণ যৌক্তিক অ্যাপ্রোচ, কিন্তু এটাকে কি ভালো করতে পারে?

public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
    private ImageAdapter mAdapter;

    // A static dataset to back the GridView adapter
    public final static Integer[] imageResIds = new Integer[] {
            R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,
            R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,
            R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};

    // Empty constructor as per Fragment docs
    public ImageGridFragment() {}

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mAdapter = new ImageAdapter(getActivity());
    }

    @Override
    public View onCreateView(
            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View v = inflater.inflate(R.layout.image_grid_fragment, container, false);
        final GridView mGridView = (GridView) v.findViewById(R.id.gridView);
        mGridView.setAdapter(mAdapter);
        mGridView.setOnItemClickListener(this);
        return v;
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
        final Intent i = new Intent(getActivity(), ImageDetailActivity.class);
        i.putExtra(ImageDetailActivity.EXTRA_IMAGE, position);
        startActivity(i);
    }

    private class ImageAdapter extends BaseAdapter {
        private final Context mContext;

        public ImageAdapter(Context context) {
            super();
            mContext = context;
        }

        @Override
        public int getCount() {
            return imageResIds.length;
        }

        @Override
        public Object getItem(int position) {
            return imageResIds[position];
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup container) {
            ImageView imageView;
            if (convertView == null) { // if it's not recycled, initialize some attributes
                imageView = new ImageView(mContext);
                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                imageView.setLayoutParams(new GridView.LayoutParams(
                        LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
            } else {
                imageView = (ImageView) convertView;
            }
            imageView.setImageResource(imageResIds[position]); // Load image into ImageView
            return imageView;
        }
    }
}

আর একবার, এই বাস্তবায়নের সমস্যা হচ্ছে যে ইমেজ ইউআই থ্রেডে সেট হচ্ছে। যখন এটা ছোট, সহজ ইমেজের জন্য কাজ করে (সিস্টেম রিসোর্স লোড এবং ক্যাশে করার কারনে), যদি কোন অতিরিক্ত প্রসেস করার দরকার হয়, আপনার ইউআই একটি হল্টে ধ্বংস হয়।

পূর্ববর্তী সেকশন থেকে একই আসিঙক্রোনাস প্রসেস এবং ক্যাশে করার মেথড এখানে বস্তবায়ন করা যেতে পারে। কিন্তু, আপনার কনকারেন্সি ইস্যুতে সতর্ক হওয়া প্রয়োজন কারন GridView চাইল্ড ভিউ রিসাইকেল করে। এটা পরিচালনা করতে, Processing Bitmaps Off the UI Thread অনুশীলনীতে আলোচিত কৌশল ব্যবহার করুন। এখানে আপডেট সমাধান দেয়া হলো:

public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
    ...

    private class ImageAdapter extends BaseAdapter {
        ...

        @Override
        public View getView(int position, View convertView, ViewGroup container) {
            ...
            loadBitmap(imageResIds[position], imageView)
            return imageView;
        }
    }

    public void loadBitmap(int resId, ImageView imageView) {
        if (cancelPotentialWork(resId, imageView)) {
            final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
            final AsyncDrawable asyncDrawable =
                    new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
            imageView.setImageDrawable(asyncDrawable);
            task.execute(resId);
        }
    }

    static class AsyncDrawable extends BitmapDrawable {
        private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;

        public AsyncDrawable(Resources res, Bitmap bitmap,
                BitmapWorkerTask bitmapWorkerTask) {
            super(res, bitmap);
            bitmapWorkerTaskReference =
                new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
        }

        public BitmapWorkerTask getBitmapWorkerTask() {
            return bitmapWorkerTaskReference.get();
        }
    }

    public static boolean cancelPotentialWork(int data, ImageView imageView) {
        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

        if (bitmapWorkerTask != null) {
            final int bitmapData = bitmapWorkerTask.data;
            if (bitmapData != data) {
                // Cancel previous task
                bitmapWorkerTask.cancel(true);
            } else {
                // The same work is already in progress
                return false;
            }
        }
        // No task associated with the ImageView, or an existing task was cancelled
        return true;
    }

    private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
       if (imageView != null) {
           final Drawable drawable = imageView.getDrawable();
           if (drawable instanceof AsyncDrawable) {
               final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
               return asyncDrawable.getBitmapWorkerTask();
           }
        }
        return null;
    }

    ... // include updated BitmapWorkerTask class

নোট:একই কোড ListView সাথে কাজ করতেও মানিয়ে নিতে পারে।

এই বাস্তবায়ন ফ্লেক্সিবিলিটির জন্য অনুমোদন করে যেভাবে ইমেজ প্রসেস এবং লোড হয় ইউআই এর মসৃনতাকে বাধাগ্রস্থ না করেই। ব্যাকগ্রাউন্ড টাস্কে আপনি নেটওয়ার্ক থেকে ইমেজ লোড করতে পারেন বা বড় ডিজিটাল ক্যামেরা ফটো এবং ইমেজ দৃশ্যমান তা রিসাইজ করতে পারেন যেহেতু টাস্ক প্রসেস করা শেষ করে।

এটার এবং অন্য কনসেপ্টের যা এই অনুশীলনীতে আলোচনা করা হয়েছে তার পূর্ণ উদাহরনের জন্য, দয়া করে অন্তর্ভূক্ত স্যাম্পল অ্যাপলিকেশন দেখুন।