skip to Main Content

I’m using Retrofit to load data from web api and display data to recyclerview. Here are my layout:

Item of recycler view:

 <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="100"
    >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="10dp"
        android:layout_weight="30"

        >
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <android.support.v7.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                xmlns:cardview="http://schemas.android.com/apk/res-auto"
                cardview:cardCornerRadius="@dimen/cardview_corner_radius"
                >
                <ImageView
                    android:id="@+id/imgExhibit"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:scaleType="fitXY"

                    android:src="@drawable/img_no_image" />
            </android.support.v7.widget.CardView>
            <ProgressBar
                android:id="@+id/viewProgressBar"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:indeterminateDrawable="@drawable/my_progress"
                android:visibility="gone"/>
        </RelativeLayout>



    </LinearLayout>


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="70"
        android:orientation="vertical"
        android:weightSum="100">

        <TextView
            android:id="@+id/tvExhibitName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"

            android:layout_weight="10"
            android:text="TRỐNG NHẠC"
            android:textAppearance="@style/ExhibitNameMainscreen" />

        <TextView
            android:id="@+id/tvExhibitDescription"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAppearance="@style/ExhibitDescriptionMainscreen"
            android:layout_weight="30"
            android:ellipsize="end"
            android:maxLines="2"
            android:text="Trống nhạc là hiện vật do Toà thánh Ngọc Sắc trao tặng"
             />

    </LinearLayout>


</LinearLayout>

Model is auto parse by http://www.jsonschema2pojo.org/ from api:
http://demo.museum.vebrary.vn/api/Exhibit/GetPaging?pageindex=1&pagesize=10

public class ExhibitMainScreenModel {

@SerializedName("EXHID")
@Expose
private Integer eXHID;
@SerializedName("EXHIBITNAME")
@Expose
private String eXHIBITNAME;
@SerializedName("DESCRIPTION")
@Expose
private String dESCRIPTION;

public Integer getEXHID() {
    return eXHID;
}

public void setEXHID(Integer eXHID) {
    this.eXHID = eXHID;
}

public String getEXHIBITNAME() {
    return eXHIBITNAME;
}

public void setEXHIBITNAME(String eXHIBITNAME) {
    this.eXHIBITNAME = eXHIBITNAME;
}

public String getDESCRIPTION() {
    return dESCRIPTION;
}

public void setDESCRIPTION(String dESCRIPTION) {
    this.dESCRIPTION = dESCRIPTION;
}

}

and api http://demo.museum.vebrary.vn/api/Exhibit/GetImages?id=4 return one String of image by id

Previously, I used to add attribute String Image to Model because the previous api return both data and image. And I can load it easy to recycler view. But it toke many time loading.
And then, I have 2 api get data and get image independent.
I want to display data before, and UI main don’t block by loading image to image view, I want to can scroll the recycler view while the image view loading (be like facebook app)
Here are Main activity class:

public class MainActivityNew extends AppCompatActivity implements View.OnClickListener {
Toolbar toolbar;
RecyclerView recyclerView;
NavigationView navigationView;
DrawerLayout drawerLayout;
TextView tvTitleToolbar, txtTitleCategory;

//RecyclerView api
private ExhibitMainscreenRecyclerViewAdapter mAdapter;
private ApiService mService;
//ProgressDialog
private ProgressBar viewProgressBar;
private CustomProgressDialogTwo customProgressDialogTwo;
private LoadMoreProgressDialog loadMoreProgressDialog;
//load more
private int indexPage=1;
private int size=10;

private EndlessRecyclerViewScrollListener scrollListener;
//
int id;



@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main_new);
    //set transparent stt bar
    //StatusBarUtil.setTransparent(this);
    addControl();
    actionBar();
    showDataToRecyclerView();
    showProgressDialog();
    loadAnswers(indexPage,size);
    showIntroMenu();
    setPositionTextViewTittleCategogy();
    addEvent();




}



public void showErrorMessage() {
    Toast.makeText(MainActivityNew.this, "Error loading posts", Toast.LENGTH_SHORT).show();
}

private void showProgressDialog() {
    viewProgressBar.setVisibility(View.GONE);
    customProgressDialogTwo.show();
}
private void showLoadMoreProgressDialog(){
    viewProgressBar.setVisibility(View.GONE);
    loadMoreProgressDialog.show();
}
private void loadAnswers(int indexPage,int size) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            mService.getExhibitByPage(indexPage,size).enqueue(new Callback<AllExhibitJsonResponse>() {
                @Override
                public void onResponse(Call<AllExhibitJsonResponse> call, Response<AllExhibitJsonResponse> response) {

                    if(response.isSuccessful()) {
                        customProgressDialogTwo.dismiss();
                        mAdapter.updateAnswers(response.body().getExhibitModels());
                        Log.d("AnswersPresenter", "posts loaded from API");
                    }else {
                        int statusCode  = response.code();
                        Toast.makeText(MainActivityNew.this, "Error"+statusCode+response.message(), Toast.LENGTH_SHORT).show();
                    }
                }

                @Override
                public void onFailure(Call<AllExhibitJsonResponse> call, Throwable t) {
                    showErrorMessage();
                    Log.d("AnswersPresenter", "error loading from API");

                }
            });
        }
    }).start();

}
private void showDataToRecyclerView() {
    mAdapter = new ExhibitMainscreenRecyclerViewAdapter(this, new ArrayList<ExhibitMainScreenModel>(0), new ExhibitMainscreenRecyclerViewAdapter.PostItemListener() {


        @Override
        public void onPostClick(long id) {
            startDetailActivity((int)id);

        }
    });
    /*RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
    recyclerView.setLayoutManager(layoutManager);*/
    GridLayoutManager layoutManager = new GridLayoutManager(this, 2);
    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setAdapter(mAdapter);
    recyclerView.setHasFixedSize(true);
    EndlessRecyclerViewScrollListener scrollListener = new EndlessRecyclerViewScrollListener(layoutManager) {

        @Override
        public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
            showLoadMoreProgressDialog();
            loadMoreAnswers(page,size);


        }
    };
    recyclerView.addOnScrollListener(scrollListener);
}

private void loadMoreAnswers(int i, int size) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            mService.getExhibitByPage(i+1,size).enqueue(new Callback<AllExhibitJsonResponse>() {
                @Override
                public void onResponse(Call<AllExhibitJsonResponse> call, Response<AllExhibitJsonResponse> response) {

                    if(response.isSuccessful()) {
                        loadMoreProgressDialog.dismiss();
                        mAdapter.updateMoreAnswers(response.body().getExhibitModels());

                        Log.d("AnswersPresenter", "posts loaded from API");
                    }else {
                        int statusCode  = response.code();
                        Toast.makeText(MainActivityNew.this, "Error"+statusCode+response.message(), Toast.LENGTH_SHORT).show();
                    }
                }

                @Override
                public void onFailure(Call<AllExhibitJsonResponse> call, Throwable t) {
                    showErrorMessage();
                    Log.d("AnswersPresenter", "error loading from API");

                }
            });
        }
    }).start();



}

And the adapter:

public class ExhibitMainscreenRecyclerViewAdapter extends RecyclerView.Adapter<ExhibitMainscreenRecyclerViewAdapter.ViewHolder> {

private List<ExhibitMainScreenModel> ExhibitList;
private Context mContext;
private PostItemListener mItemListener;
private int id;
//web api
private ApiService mService;


public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{

    public TextView tvName,tvDescription;
    public ImageView imvExhibit;
    PostItemListener mItemListener;

    public ViewHolder(View itemView, PostItemListener postItemListener) {
        super(itemView);
        tvName = itemView.findViewById(R.id.tvExhibitName);
        tvDescription = itemView.findViewById(R.id.tvExhibitDescription);
        imvExhibit=itemView.findViewById(R.id.imgExhibit);

        this.mItemListener = postItemListener;
        itemView.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        ExhibitMainScreenModel item = getItem(getAdapterPosition());
        this.mItemListener.onPostClick(item.getEXHID());

        notifyDataSetChanged();
    }
}

public ExhibitMainscreenRecyclerViewAdapter(Context context, List<ExhibitMainScreenModel> posts, PostItemListener itemListener) {
    ExhibitList = posts;
    mContext = context;
    mItemListener = itemListener;
}

@Override
public ExhibitMainscreenRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    Context context = parent.getContext();
    LayoutInflater inflater = LayoutInflater.from(context);

    View postView = inflater.inflate(R.layout.item_recyclerview_mainscreen, parent, false);

    ViewHolder viewHolder = new ViewHolder(postView, this.mItemListener);
    //web api
    mService = ApiUtils.getSOService();
    return viewHolder;
}

@Override
public void onBindViewHolder(ExhibitMainscreenRecyclerViewAdapter.ViewHolder holder, int position) {

    ExhibitMainScreenModel item = ExhibitList.get(position);
    TextView tvName = holder.tvName;
    tvName.setText(item.getEXHIBITNAME());
    TextView tvDesc = holder.tvDescription;
    tvDesc.setText(item.getDESCRIPTION());
    //
    id = item.getEXHID();
    loadImage(id,holder);




}

private void loadImage(int id, ViewHolder holder) {
    new Thread(new Runnable() {
        @Override
        public void run() {
                //get image default
                mService.getExhibitImageById(id, true).enqueue(new Callback<String>() {
                    @Override
                    public void onResponse(Call<String> call, Response<String> response) {

                        if (response.isSuccessful()) {
                            try{
                                showImage(response.body(),holder);
                                Log.d("AnswersPresenter", "Image loaded!!!!");
                            }
                            catch (Exception e)
                            {
                                e.printStackTrace();
                            }

                        } else {
                            int statusCode = response.code();
                            Toast.makeText(mContext, "Error" + statusCode + response.message(), Toast.LENGTH_SHORT).show();
                        }

                    }


                    @Override
                    public void onFailure(Call<String> call, Throwable t) {
                        showErrorMessage();
                        Log.d("AnswersPresenter", "error loading image!!!");

                    }
                });

        }
    }).run();
}

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

public void updateAnswers(List<ExhibitMainScreenModel> items) {
    ExhibitList = items;
    notifyDataSetChanged();
}
public void updateMoreAnswers(List<ExhibitMainScreenModel> items) {
    ExhibitList.addAll(items);
    notifyDataSetChanged();
}

private ExhibitMainScreenModel getItem(int adapterPosition) {
    return ExhibitList.get(adapterPosition);
}

public interface PostItemListener {
    void onPostClick(long id);
}
private void showImage(String imageString, ViewHolder holder) {
    Bitmap bmp = Util.StringToBitMap(imageString);
    ImageView imv = holder.imvExhibit;
    imv.setImageBitmap(bmp);

}

public void showErrorMessage() {
    Toast.makeText(mContext, "Error loading posts", Toast.LENGTH_SHORT).show();
}

}

And here is result with my code.
result

but the image is display intricate
So, can you help me?
Ah, It usually have error “android application doing too much work on its main thread”
Can you help me solve it? Thank you very much!!

2

Answers


  1. Once you get the response or url of image the kindly use the libarary by this way it will automatically handle all scenario.

    if you are getting url of image in the response in below line:

    showImage(response.body(),holder);
    

    here if response.body() contains the url of image

    Replace

     Bitmap bmp = Util.StringToBitMap(imageString);
        ImageView imv = holder.imvExhibit;
        imv.setImageBitmap(bmp);
    

    by

    Picasso.with(context).load(your image url).into(imageView);
    

    if your response is base64 encoded. where urlRes is your encoded response like:-

    String urlRes = "XFLSVJpiqqVA3yQVy74obNAMCrajG1a5mvtgZBcXFK";
    String data =  new FileDecryption().decrypt(urlRes);
            Log.e("MEssage decoded", data);
    

    add two classes in your project.

    Base64.java

    public class Base64 {
        /**
         * encode
         *
         * coverts a byte array to a string populated with
         * base64 digits.  It steps through the byte array
         * calling a helper method for each block of three
         * input bytes
         *
         * @param raw The byte array to encode
         * @return A string in base64 encoding
         */
        public static String encode(byte[] raw) {
            StringBuffer encoded = new StringBuffer();
            for (int i = 0; i < raw.length; i += 3) {
                encoded.append(encodeBlock(raw, i));
            }
            return encoded.toString();
        }
    
        /*
         * encodeBlock
         *
         * creates 4 base64 digits from three bytes of input data.
         * we use an integer, block, to hold the 24 bits of input data.
         *
         * @return An array of 4 characters
         */
        protected static char[] encodeBlock(byte[] raw, int offset) {
            int block = 0;
            // how much space left in input byte array
            int slack = raw.length - offset - 1;
            // if there are fewer than 3 bytes in this block, calculate end
            int end = (slack >= 2) ? 2 : slack;
            // convert signed quantities into unsigned
            for (int i = 0; i <= end; i++) {
                byte b = raw[offset + i];
                int neuter = (b < 0) ? b + 256 : b;
                block += neuter << (8 * (2 - i));
            }
    
            // extract the base64 digits, which are six bit quantities.
            char[] base64 = new char[4];
            for (int i = 0; i < 4; i++) {
                int sixbit = (block >>> (6 * (3 - i))) & 0x3f;
                base64[i] = getChar(sixbit);
            }
            // pad return block if needed
            if (slack < 1)
                base64[2] = '=';
            if (slack < 2)
                base64[3] = '=';
            // always returns an array of 4 characters
            return base64;
        }
    
        /*
         * getChar
         *
         * encapsulates the translation from six bit quantity
         * to base64 digit
         */
        protected static char getChar(int sixBit) {
            if (sixBit >= 0 && sixBit <= 25)
                return (char) ('A' + sixBit);
            if (sixBit >= 26 && sixBit <= 51)
                return (char) ('a' + (sixBit - 26));
            if (sixBit >= 52 && sixBit <= 61)
                return (char) ('0' + (sixBit - 52));
            if (sixBit == 62)
                return '+';
            if (sixBit == 63)
                return '/';
           return '?';
        }
    
        /**
         * decode
         *
         * convert a base64 string into an array of bytes.
         *
         * @param base64 A String of base64 digits to decode.
         * @return A byte array containing the decoded value of
         *         the base64 input string
         */
        public static byte[] decode(String base64) {
            // how many padding digits?
            int pad = 0;
            for (int i = base64.length() - 1; base64.charAt(i) == '='; i--)
                pad++;
            // we know know the length of the target byte array.
            int length = base64.length() * 6 / 8 - pad;
            byte[] raw = new byte[length];
            int rawIndex = 0;
            // loop through the base64 value.  A correctly formed
            // base64 string always has a multiple of 4 characters.
            for (int i = 0; i < base64.length(); i += 4) {
                int block = (getValue(base64.charAt(i)) << 18)
                        + (getValue(base64.charAt(i + 1)) << 12)
                        + (getValue(base64.charAt(i + 2)) << 6)
                        + (getValue(base64.charAt(i + 3)));
                // based on the block, the byte array is filled with the
                // appropriate 8 bit values
                for (int j = 0; j < 3 && rawIndex + j < raw.length; j++)
                    raw[rawIndex + j] = (byte) ((block >> (8 * (2 - j))) & 0xff);
                rawIndex += 3;
            }
            return raw; 
        }
    
        /*
         * getValue
         *
         * translates from base64 digits to their 6 bit value
         */
        protected static int getValue(char c) {
            if (c >= 'A' && c <= 'Z')
                return c - 'A';
            if (c >= 'a' && c <= 'z')
                return c - 'a' + 26;
            if (c >= '0' && c <= '9')
                return c - '0' + 52;
            if (c == '+')
                return 62;
            if (c == '/')
                return 63;
            if (c == '=')
                return 0;
            return -1;
        }
    }
    

    and second one is FileDecryption.java

    public class FileDecryption {
    
         private static final String ALGO = "AES";
        private static final byte[] keyValue = 
            new byte[] { 'S', 'n', 'd', 'A', 'p', 'p','s','S', 'e', 'c', 'r','e', 't', 'K', 'e', 'y' };
    
        public  String decrypt(String encryptedData) {
            //encryptedData="PDvLeKmmKUcUQd/zKZ5b2JPRCXHJwu4dVZhaZJCrRK4JRvBP3IhRpizk6qCLcaUmhoDGR+QUeS1fjHdWujZTGQ==";
            String decryptedValue=null;
            //int flags= Base64.NO_WRAP | Base64.URL_SAFE;
            if("ON".equals("ON")){
            try{
            Key key = generateKey();
            Cipher c = Cipher.getInstance(ALGO);
            c.init(Cipher.DECRYPT_MODE, key);
           // byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedData);
            byte[] decordedValue = Base64.decode(encryptedData);
            byte[] decValue = c.doFinal(decordedValue);
            decryptedValue = new String(decValue);}
            catch(Exception e){
                e.printStackTrace();
            }
            }else{
                decryptedValue=encryptedData;
            }
    
            //Log.d("decrypted : ", decryptedValue);
            return decryptedValue;
        }
        private static Key generateKey() throws Exception {
            Key key = new SecretKeySpec(keyValue, ALGO);
            return key;
    }
    
    }
    
    Login or Signup to reply.
  2. If I understood you correctly, you’re using retrofit to load the image from the network. Instead of doing it yourself, there are some few popular libraries intended to handle, load and display images whithin an app, giving the user a free UI thread and experience.
    These libraries let you focus on your main logic instead of technical things every developer needs. Also, because they are very popular, they are less buggy than what we all would probably write ourselves and keep on being updated as time goes by.
    These libraries include, among others, Picasso, Glide and Fresco.

    Good luck!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search