I'm trying to fill a List object by using retrofit. Currently, I'm getting a null pointer exception whenever I call the List object. How do I get retrofit to work properly?
My call to Retrofit:
#Override
public void success(List<Game> gameList, Response response) {
mGameSeason = gameList;
}
My Retrofit implementation:
public class ApiClient {
private static ApiInterface sApiService;
public static ApiInterface getApiClient() {
if (sApiService == null) {
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("http://www.someapi.com")
.build();
sApiService = restAdapter.create(ApiInterface.class);
}
return sApiService;
}
public interface ApiInterface {
#GET("path")
void getGames(Callback<List<Game>> callback);
}
}
My Game POJO:
public class Game {
#Expose
private String gameID;
#Expose
private String date;
#Expose
private String awayTeam;
#Expose
private String homeTeam;
#Expose
private String gameType;
/**
*
* #return
* The gameID
*/
public String getGameID() {
return gameID;
}
/**
*
* #param gameID
* The gameID
*/
public void setGameID(String gameID) {
this.gameID = gameID;
}
/**
*
* #return
* The date
*/
public String getDate() {
return date;
}
/**
*
* #param date
* The date
*/
public void setDate(String date) {
this.date = date;
}
/**
*
* #return
* The awayTeam
*/
public String getAwayTeam() {
return awayTeam;
}
/**
*
* #param awayTeam
* The awayTeam
*/
public void setAwayTeam(String awayTeam) {
this.awayTeam = awayTeam;
}
/**
*
* #return
* The homeTeam
*/
public String getHomeTeam() {
return homeTeam;
}
/**
*
* #param homeTeam
* The homeTeam
*/
public void setHomeTeam(String homeTeam) {
this.homeTeam = homeTeam;
}
/**
*
* #return
* The gameType
*/
public String getGameType() {
return gameType;
}
/**
*
* #param gameType
* The gameType
*/
public void setGameType(String gameType) {
this.gameType = gameType;
}
}
The JSON Response:
[
{
"gameID":"2011030416",
"date":"Mon Jun 11, 2012",
"awayTeam":"New Jersey Devils",
"homeTeam":"Los Angeles Kings",
"gameType":"Playoffs"
},
{
"gameID":"2011030415",
"date":"Sat Jun 09, 2012",
"awayTeam":"Los Angeles Kings",
"homeTeam":"New Jersey Devils",
"gameType":"Playoffs"
},
{
"gameID":"2011030414",
"date":"Wed Jun 06, 2012",
"awayTeam":"New Jersey Devils",
"homeTeam":"Los Angeles Kings",
"gameType":"Playoffs"
},
{
"gameID":"2011030413",
"date":"Mon Jun 04, 2012",
"awayTeam":"New Jersey Devils",
"homeTeam":"Los Angeles Kings",
"gameType":"Playoffs"
},
{
"gameID":"2011030314",
"date":"Mon May 21, 2012",
"awayTeam":"New York Rangers",
"homeTeam":"New Jersey Devils",
"gameType":"Playoffs"
},
{
"gameID":"2011030313",
"date":"Sat May 19, 2012",
"awayTeam":"New York Rangers",
"homeTeam":"New Jersey Devils",
"gameType":"Playoffs"
}
]
Do you declare correctly your list mGameSeason before?
You can try to do something like this
public void success(List<Game> gameList, Response response) {
mGameSeason.clear();
mGameSeason.addAll(gameList);
gameAdapter.notifyDataSetChanged();
}
You will also need to define an adapter for your list if you haven't already done it.
Let me know if this helps
Here's what you can do for the adapter
protected List<Game> mGameList;
protected LayoutInflater inflater;
protected Context mContext;
public GameAdapter(List<Game> games, Context context) {
this.mGameList = games;
this.mContext = context;
this.inflater = LayoutInflater.from(this.mContext);
}
#Override
public int getCount() {
return mGameList.size();
}
#Override
public Game getItem(int position) {
return mGameList.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolderItem viewHolderItem;
if(convertView==null){
// inflate the layout
LayoutInflater inflater = LayoutInflater.from(mContext);
convertView = inflater.inflate(R.layout.list_item_game, parent, false);
// well set up the ViewHolder
viewHolderItem = new ViewHolderItem(convertView);
// store the holder with the view.
convertView.setTag(viewHolderItem);
}else{
// we've just avoided calling findViewById() on resource everytime
// just use the viewHolder
viewHolderItem = (ViewHolderItem) convertView.getTag();
}
Game game = getItem(position);
// assign values if the object is not null
if(game != null) {
// set your layout here
viewHolderItem.nameGame.setText(game.getName());
}
return convertView;
}
static class ViewHolderItem {
#InjectView(R.id.name_radio)
TextView nameRadio;
public ViewHolderItem(View v) {
ButterKnife.inject(this, v);
}
}
RestAdapter and your API interface should be used as a SINGLETON, it looks like you are not checking the rest adapter to be a singleton, try something like this in your fragment or activity:
// Activity or fragment use
MyAwesomeApiInterface api;
MyAwesomeApiInterface getApi() {
if (api == null) {
api = getRestAdapter().create(MyAwesomeApiInterface.class);
}
return api;
}
Retrofit retrofit;
Retrofit getRestAdapter() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
Related
I have am trying to fetch Different types of List data using Retrofit.
I have make a List and put data on that.
Can anyone suggest me how I can fetch Different Type of Model Data? I have When I tried to put Object List to my Custom model list I got this error :
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to .....
Model Class
public class WorkType {
#SerializedName("type")
#Expose
private String type;
#SerializedName("worklist")
#Expose
private List<Object> worklist = null;
/**
* No args constructor for use in serialization
*
*/
public WorkType() {
}
/**
*
* #param worklist
* #param type
*/
public WorkType(String type, List<Object> worklist) {
super();
this.type = type;
this.worklist = worklist;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Object> getWorkList() {
return worklist;
}
public void setWorkList(List<Object> worklist) {
this.worklist = worklist;
}
}
Retrofit Interface
#FormUrlEncoded
#POST("worklist")
Call<ArrayList<WorkType>> worklist(
#Field("teamid") String teamID
);
Fragment from where I make a API request
public class WorkTypeListFragment extends Fragment {
private FragmentWorkTypeListBinding binding;
private LoadingDialog loadingDialog;
private Context context;
public WorkTypeListFragment() {
// Required empty public constructor
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
this.context = context;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
binding = DataBindingUtil.inflate(inflater,R.layout.fragment_work_type_list,container,false);
init();
String teamID = getActivity().getIntent().getStringExtra("teamid");
fetchClientDetails("team1");
return binding.getRoot();
}
private void init() {
loadingDialog = new LoadingDialog(getActivity());
}
private void fetchClientDetails(String teamID){
loadingDialog.startLoadingDialog(); // Added Loading Screen
Call<ArrayList<WorkType>> call = RetrofitClient
.getInstance()
.getRetrofitApi()
.worklist(teamID);
call.enqueue(new Callback<ArrayList<WorkType>>() {
#Override
public void onResponse(Call<ArrayList<WorkType>> call, Response<ArrayList<WorkType>> response) {
try{
if(response.code() == 200){
ArrayList<WorkType> workTypeArrayList = response.body();
//Log.d("TAG", "onResponse: "+workTypeArrayList.get(1).getWorkList().get(0).getClientname());
assert workTypeArrayList != null;
bindDataToView(workTypeArrayList);
} else {
Toast.makeText(getContext(), "Response Code is no 200", Toast.LENGTH_SHORT).show();
loadingDialog.dismissDialog();
}
}catch(Exception e){
e.printStackTrace();
}
}
#Override
public void onFailure(Call<ArrayList<WorkType>> call, Throwable t) {
Toast.makeText(getContext(), "Something Went wrong! Please try again later!", Toast.LENGTH_SHORT).show();
loadingDialog.dismissDialog();
}
});
}
private void bindDataToView(ArrayList<WorkType> workTypeArrayList) {
WorkTypesAdapter adapter = new WorkTypesAdapter(context,workTypeArrayList);
binding.workTypeRv.setLayoutManager(new LinearLayoutManager(context));
binding.workTypeRv.setAdapter(adapter);
}
}
RecyclearView Adapter Class
public class WorkTypesAdapter extends RecyclerView.Adapter<WorkTypesAdapter.ViewHolder> {
private Context mCtx;
private List<WorkType> workTypeList;
private final static int survey = 1;
private final static int service = 2;
private final static int maintain = 3;
public WorkTypesAdapter(Context mCtx, List<WorkType> modelServiceList) {
this.mCtx = mCtx;
this.workTypeList = modelServiceList;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mCtx);
View view = inflater.inflate(R.layout.layout_worktype, null);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
WorkType currentWorkType = workTypeList.get(position);
String modelClass = currentWorkType.getType();
List<Object> objectList = currentWorkType.getWorkList();
switch (modelClass){
/*case "survey":
List<SurveyWorkList> surveyWorkLists = new ArrayList<>();
for (Object object : objectList) {
surveyWorkLists.add((SurveyWorkList) object);
holder.workType.setText(currentWorkType.getType());
holder.workTypeCount.setText(surveyWorkLists.size());
}
break;*/
case "service":
List<ServiceWorkList> serviceWorkLists = new ArrayList<>();
for (Object object : objectList) {
serviceWorkLists.add((ServiceWorkList) object);
}
/*holder.workType.setText(currentWorkType.getType());
holder.workTypeCount.setText(serviceWorkLists.size());*/
break;
/*case "maintain":
List<MaintainWorkList> maintainWorkLists = new ArrayList<>();
for (Object object : objectList) {
maintainWorkLists.add((MaintainWorkList) object);
}
holder.workType.setText(currentWorkType.getType());
holder.workTypeCount.setText(maintainWorkLists.size());
break;*/
default:
//Do nothing
}
/*holder.workType.setText(modelService.getType());
holder.workTypeCount.setText(workTypeList.size());*/
}
#Override
public int getItemCount() {
return workTypeList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
TextView workType, workTypeCount;
public ViewHolder(#NonNull View itemView) {
super(itemView);
workType = itemView.findViewById(R.id.work_type_tv);
workTypeCount = itemView.findViewById(R.id.work_type_count_tv);
}
}
}
API response
[
{
"type": "survey",
"worklist": []
},
{
"type": "service",
"worklist": [
{
"serviceid": "HS2020031613054318",
"clientid": "R2020031612594874",
"clientname": "********",
"companyname": "",
"membership": "",
"phone": [
"*********"
],
"**************",
"account": 2,
"slot": "morning",
"orderstatus": "Completed",
"orderstatuscode": "8",
"remarks": ""
}
]
},
{
"type": "maintain",
"worklist": []
}
]
The survey and maintain will have different Parameter.
I have found the answer.
First, you have to make a model class:
#SerializedName("contents")
#Expose
private JsonElement contents = null;
In my API I got an element type. So you know in which type you have to parse the data. Let's say you have three types. So we will create a Type object:
switch(type)
case 1:
Type type = new TypeToken<ArrayList<Banner>>() {
}.getType();
ArrayList<Banner> bannerList = new Gson().fromJson(baseModel.getContents(), type);
I'm beginner in Android development. I'm using this library for http requests https://github.com/loopj/android-async-http.
And then I render response JSON in GridView it takes very long time. I'm using progress dialog as preloader, but it's removed before listview is rendered. How I can solve this problem? Thank you.
Here is screenshot of this GridView.
Here is implementation of this activity.
public class LikesActivity extends BottomBarActivity //some custom activity {
List<Anticafe> mAnticafes = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_likes);
/*some code*/
Request.post(Constants.POST_SEARCH_API_LINK, params, new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
Gson gson = new GsonBuilder().create();
Request request = gson.fromJson(new String(responseBody), Request.class);
mAnticafes = request.getAnticafes();
renderListView()
}
#Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
hideProcessDialog();
}
});
}
private void renderListView() {
GridView likesList = (GridView) findViewById(R.id.likes_list);
AnticafeAdapter anticafeAdapter = new AnticafeAdapter(this, mAnticafes);
anticafeAdapter.setWidth(getWidth());
likesList.setAdapter(anticafeAdapter);
}
}
Here is full implementaion of adapter.
public class AnticafeAdapter extends BaseAdapter{
public static final String TAG = "image-request";
private Context mContext;
private List<Anticafe> mAnticafes = new ArrayList<>();
private Client mClient;
private View mConvertView;
private int mWidth;
public AnticafeAdapter(Context context, List<Anticafe> anticafes) {
mContext = context;
mAnticafes = anticafes;
}
public void setWidth(int mWidth) {
this.mWidth = (int) (mWidth / 4.7);
}
#Override
public int getCount() {
return mAnticafes.size();
}
#Override
public Anticafe getItem(int position) {
return mAnticafes.get(position);
}
#Override
public long getItemId(int position) {
return getItem(position).getId();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final Anticafe anticafe = getItem(position);
mClient = AnticafeApplication.getInstanse().getClient();
mConvertView = convertView;
final ProgressDialog processDialog = new ProgressDialog(mContext);
processDialog.setMessage("Информация обновляется. Пожалуйста, подождите.");
if(mConvertView == null) {
mConvertView = LayoutInflater.from(mContext).inflate(R.layout.adapter_popular, null);
}
ImageView cover = (ImageView) mConvertView.findViewById(R.id.cover);
CircularImageView logo = (CircularImageView) mConvertView.findViewById(R.id.logo);
final ImageView like = (ImageView) mConvertView.findViewById(R.id.like);
final TextView likesCount = (TextView) mConvertView.findViewById(R.id.likes_count);
TextView name = (TextView) mConvertView.findViewById(R.id.name);
TextView price = (TextView) mConvertView.findViewById(R.id.price);
if(mClient != null) {
final boolean liked = mClient.containsLike(anticafe.getId());
if(liked) {
like.setImageDrawable(mContext.getResources().getDrawable(R.drawable.ic_like));
} else {
like.setImageDrawable(mContext.getResources().getDrawable(R.drawable.ic_unlike));
}
} else {
like.setImageDrawable(mContext.getResources().getDrawable(R.drawable.ic_unlike));
}
likesCount.setText(String.valueOf(anticafe.getTotalLikes()));
name.setText(anticafe.getName());
price.setText(anticafe.getPrices());
Picasso.with(mContext).load(anticafe.getAttachment().getCover()).networkPolicy(NetworkPolicy.OFFLINE).into(cover);
Picasso.with(mContext).load(anticafe.getAttachment().getLogo()).resize(mWidth, mWidth).into(logo);
likeMechanism(anticafe, processDialog, like, likesCount);
name.setOnClickListener(anticafeOpenActivityEvent(anticafe.getId()));
price.setOnClickListener(anticafeOpenActivityEvent(anticafe.getId()));
logo.setOnClickListener(anticafeOpenActivityEvent(anticafe.getId()));
cover.setOnClickListener(anticafeOpenActivityEvent(anticafe.getId()));
BottomBarActivity activity = (BottomBarActivity) this.mContext;
activity.hideProcessDialog();
return mConvertView;
}
private void likeMechanism(final Anticafe anticafe, final ProgressDialog processDialog, final ImageView like, final TextView likesCount) {
like.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (mClient == null) {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle("Вы не авторизованы")
.setMessage("До тех пор, пока вы не пройдете авторизацию, вы не сможете пользоваться некоторым функционалом нашего приложение. Авторизация займет всего лишь пару минут.")
.setCancelable(false)
.setNegativeButton("Скрыть", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setPositiveButton("Авторизоваться", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(mContext, AuthActivity.class);
mContext.startActivity(intent);
Activity activity = ((Activity) mContext);
activity.overridePendingTransition(R.anim.slide_in_left, R.anim.slide_in_right);
}
});
AlertDialog dialog = builder.create();
dialog.show();
} else {
processDialog.show();
RequestParams params = new RequestParams();
params.add("anticafe_id", anticafe.getId() + "");
AuthedRequest.post(Constants.LIKE_API_LINK, params, new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
processDialog.hide();
Gson gson = new GsonBuilder().create();
try {
LikeResponse response = gson.fromJson(new String(responseBody, Constants.UTF_8), LikeResponse.class);
if (response.getLikeStatus().equals("unliked")) {
like.setImageDrawable(mContext.getResources().getDrawable(R.drawable.ic_unlike));
} else if (response.getLikeStatus().equals("liked")) {
like.setImageDrawable(mContext.getResources().getDrawable(R.drawable.ic_like));
}
likesCount.setText(String.valueOf(response.getTotalLikes()));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} finally {
processDialog.hide();
}
}
#Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
try {
Log.d("json", "onSuccess: " + new String(responseBody, Constants.UTF_8));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} finally {
processDialog.hide();
}
}
});
}
}
});
}
private OnClickListener anticafeOpenActivityEvent(final int id) {
return new OnClickListener() {
#Override
public void onClick(View v) {
Bundle bundle = new Bundle();
bundle.putInt("entity_id", id);
((BottomBarActivity) mContext).openNewActivity(AnticafeActivity.class, bundle);
}
};
}
private static class LikeResponse {
#SerializedName("status")
#Expose
private Integer status;
#SerializedName("error")
#Expose
private Boolean error;
#SerializedName("likeStatus")
#Expose
private String likeStatus;
#SerializedName("totalLikes")
#Expose
private Integer totalLikes;
public LikeResponse(Integer status, Boolean error, String likeStatus, Integer totalLikes) {
this.status = status;
this.error = error;
this.likeStatus = likeStatus;
this.totalLikes = totalLikes;
}
/**
*
* #return
* The status
*/
public Integer getStatus() {
return status;
}
/**
*
* #param status
* The status
*/
public void setStatus(Integer status) {
this.status = status;
}
/**
*
* #return
* The error
*/
public Boolean getError() {
return error;
}
/**
*
* #param error
* The error
*/
public void setError(Boolean error) {
this.error = error;
}
/**
*
* #return
* The likeStatus
*/
public String getLikeStatus() {
return likeStatus;
}
/**
*
* #param likeStatus
* The likeStatus
*/
public void setLikeStatus(String likeStatus) {
this.likeStatus = likeStatus;
}
/**
*
* #return
* The totalLikes
*/
public Integer getTotalLikes() {
return totalLikes;
}
/**
*
* #param totalLikes
* The totalLikes
*/
public void setTotalLikes(Integer totalLikes) {
this.totalLikes = totalLikes;
}
}
}
You dont show any code to say exactly what is you problem.
But any way its better to use a third party library for loading images from web such as picasso:
http://square.github.io/picasso
this library load your images smoothly and mange caches by itself.
UPDATE:
Other option is that you dont use viewHolder design pattern in your getView method in your adapter class.
In your current code you call findViewById method in every cell of your gridView but you must call it only first time.
to resolve this problem you should use viewHolder design pattern.
An exampple of getView method with view Holder :
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolderItem viewHolder;
/*
* The convertView argument is essentially a "ScrapView" as described is Lucas post
* http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/
* It will have a non-null value when ListView is asking you recycle the row layout.
* So, when convertView is not null, you should simply update its contents instead of inflating a new row layout.
*/
if(convertView==null){
// inflate the layout
LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
convertView = inflater.inflate(layoutResourceId, parent, false);
// well set up the ViewHolder
viewHolder = new ViewHolderItem();
viewHolder.textViewItem = (TextView) convertView.findViewById(R.id.textViewItem);
// store the holder with the view.
convertView.setTag(viewHolder);
}else{
// we've just avoided calling findViewById() on resource everytime
// just use the viewHolder
viewHolder = (ViewHolderItem) convertView.getTag();
}
// object item based on the position
ObjectItem objectItem = data[position];
// assign values if the object is not null
if(objectItem != null) {
// get the TextView from the ViewHolder and then set the text (item name) and tag (item ID) values
viewHolder.textViewItem.setText(objectItem.itemName);
viewHolder.textViewItem.setTag(objectItem.itemId);
}
return convertView;
}
Example of View holder class (define your view elements in this class) :
static class ViewHolderItem {
TextView textViewItem;
}
for more info see this :
https://www.javacodegeeks.com/2013/09/android-viewholder-pattern-example.html
I'm new in Android and I have following code that shows the list of item in Adapter.
I have Four Different Adapter from where I am calling one comman AsyncTask to update Result. I have implemented one Interface ApiResponse and overrides apiResponseProcessing() to get result.
In Item of List "Add to Cart" Button Added in every row. OnClick of that button I am requesting to server. On Success of that response i want to update Button with "Added To Cart".
I have question How to update that string which is binded in onBindViewHolder(). I am getting success in that method but dont know how to update clicked Button from that method.
Here's my Adapter
/**
* Adapter
**/
public class AlbumPhotoDetailAdapter
extends RecyclerView.Adapter<AlbumPhotoDetailAdapter.ViewHolder> implements ApiResponse {
private final ArrayList<Photo> mValues;
Album album;
private Activity mContext;
private int mMemberId;
public AlbumPhotoDetailAdapter(Activity context, ArrayList<Photo> items) {
mValues = items;
this.mContext = context;
mMemberId = MemberPreference.getMemberId(mContext);
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.album_photo_detail_sub_view, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final Photo photo = mValues.get(position);
/**
* Album Owner Name
*/
String mOwnerName = photo.getOwnerName();
String mOwnerProfilePic = photo.getOwnerImage();
String mDateTime = photo.getDatetime();
String mPrice = String.valueOf(photo.getPrice());
/**
* Price String
*/
String priceStr = String.format(mContext.getString(R.string.string_dollar_price), mPrice);
holder.mAlbumPhotoDetailPhotoPrice.setText(priceStr);
/**
* Main Image
*/
Picasso.with(mContext).load(photo.getLink())
.error(R.drawable.ic_place_holder_circle)
.placeholder(R.drawable.ic_place_holder_circle)
.transform(new ImageTransformation(holder.mAlbumPhotoDetailSubMainImage))
.into(holder.mAlbumPhotoDetailSubMainImage);
/**
* Owner Name and Profile Pic
*/
holder.mAlbumPhotoDetailSubOwnerNameTextView.setText(mOwnerName);
Picasso.with(mContext).load(mOwnerProfilePic)
.error(R.drawable.ic_place_holder_circle)
.placeholder(R.drawable.ic_place_holder_circle)
.resize(100, 100)
.transform(new CircleTransform())
.into(holder.mAlbumPhotoDetailSubOwnerImage);
mDateTime = mDateTime != null ? DateUtils.getNiceTime(mDateTime) : "----";
holder.mAlbumPhotoDetailSubOwnerPostedTimeTextView.setText(mDateTime);
// Photo Add to cart.
holder.mAddToCartButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(InternetConnection.checkConnection(mContext)) {
new BackgroundAsyncTask(mContext, (ApiResponse) mContext, mMemberId, photo.getId()).execute();
} else {
DailyStudio.noInternetConnectionToast(mContext);
}
}
});
}
#Override
public int getItemCount() {
return mValues.size();
}
#Override
public void apiResponseProcessing(String response) {
Log.i(TAG,"Api Response : "+response);
if(response.equals(Fields.JSON_SUCCESS)) {
}
}
/**
* View Holder
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
public final View mView;
private ImageView mAlbumPhotoDetailSubOwnerImage;
private ImageView mAlbumPhotoDetailSubMainImage;
private TextView mAlbumPhotoDetailSubOwnerNameTextView;
private TextView mAlbumPhotoDetailSubOwnerPostedTimeTextView;
private TextView mAlbumPhotoDetailPhotoPrice;
private TextView mAlbumPhotoDetailSubDescription;
private Button mAddToCartButton;
public ViewHolder(View view) {
super(view);
mView = view;
mAlbumPhotoDetailSubOwnerImage = (ImageView) view.findViewById(R.id.album_photo_detail_sub_owner_image);
mAlbumPhotoDetailSubMainImage = (ImageView) view.findViewById(R.id.album_photo_detail_sub_main_image);
mAlbumPhotoDetailSubOwnerNameTextView = (TextView) view.findViewById(R.id.album_photo_detail_sub_owner_name_text_view);
mAlbumPhotoDetailSubOwnerPostedTimeTextView = (TextView) view.findViewById(R.id.album_photo_detail_sub_owner_posted_time_text_view);
mAlbumPhotoDetailPhotoPrice = (TextView) view.findViewById(R.id.album_photo_detail_photo_price);
mAlbumPhotoDetailSubDescription = (TextView) view.findViewById(R.id.album_photo_detail_sub_description);
mAddToCartButton = (Button) view.findViewById(R.id.album_photo_detail_photo_add_to_cart_button);
}
}
}
Here's my Interface
/**
* Interface..
*/
public interface ApiResponse {
public void apiResponseProcessing(String response);
}
Here's my Background AsyncTask
/**
* Background AsyncTask...
*/
public class BackgroundAsyncTask extends AsyncTask<Void, Void, String> {
private Context context;
private String accessToken;
private int memberId;
private int photoId;
private ApiResponse objIBaseApi;
public BackgroundAsyncTask(Context context, ApiResponse apiResponse, int memberId, int photoId) {
this.context = context;
this.memberId = memberId;
this.photoId = photoId;
accessToken = MemberPreference.getAccessToken(context);
this.objIBaseApi = apiResponse;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(Void... params) {
JSONObject json = JSONParser.addToCartPhoto(accessToken, memberId, photoId);
if(json != null) {
Log.i(TAG,"First Json : "+json.toString());
try {
if (json.getString(Fields.RESULT).equalsIgnoreCase(Fields.JSON_SUCCESS)) {
return Fields.JSON_SUCCESS;
} else if(json.getString(Fields.JSON_ERROR).equalsIgnoreCase(Fields.ERROR_ACCESS_DENIED)) {
String refreshToken = MemberPreference.getRefreshToken(context);
JSONObject newJSONObject = JSONParser.loginMemberWithRefreshToken(refreshToken, Integer.toString(memberId));
if(newJSONObject != null) {
if(newJSONObject.getString(Fields.JSON_ERROR).equalsIgnoreCase(Fields.ERROR_ACCESS_DENIED)) {
return Fields.ERROR_ACCESS_DENIED;
} else {
return Fields.JSON_SUCCESS;
}
} else
return Fields.ERROR_ACCESS_DENIED;
} else {
return Fields.JSON_ERROR;
}
} catch (JSONException e) {
e.printStackTrace();
return Fields.JSON_ERROR;
}
}
return Fields.JSON_ERROR;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
objIBaseApi.apiResponseProcessing(result);
}
}
Is there any solution or better way to do like this?
Your help would be appreciated. Thank you.
You Can keep one flag isAddedToCart variable in you bean class which you are using in your adapter(Photo). Now just pass the position in your asynctask once user click on "add to cart" button. On getting the successful you just need to find the bean from the list of bean you passed in adapter and change the flag isAddedToCart to true and notify your adapter thats it. Here is the code snippet:-
Photo Class
public class Photo{
private boolean isAddedToCart;
public void setAddedTOCart(boolean isAdded){
isAddedToCart = isAdded;
}
public boolean isAddedToCart(){
return isAddedToCart;
}
}
AlbumPhotoDetailAdapter onBindViewHolder
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final Photo photo = mValues.get(position);
/**
* Album Owner Name
*/
String mOwnerName = photo.getOwnerName();
String mOwnerProfilePic = photo.getOwnerImage();
String mDateTime = photo.getDatetime();
String mPrice = String.valueOf(photo.getPrice());
String isAdded = photo.isAddedToCart();
/**
* Price String
*/
String priceStr = String.format(mContext.getString(R.string.string_dollar_price), mPrice);
holder.mAlbumPhotoDetailPhotoPrice.setText(priceStr);
/**
* Main Image
*/
Picasso.with(mContext).load(photo.getLink())
.error(R.drawable.ic_place_holder_circle)
.placeholder(R.drawable.ic_place_holder_circle)
.transform(new ImageTransformation(holder.mAlbumPhotoDetailSubMainImage))
.into(holder.mAlbumPhotoDetailSubMainImage);
/**
* Owner Name and Profile Pic
*/
holder.mAlbumPhotoDetailSubOwnerNameTextView.setText(mOwnerName);
Picasso.with(mContext).load(mOwnerProfilePic)
.error(R.drawable.ic_place_holder_circle)
.placeholder(R.drawable.ic_place_holder_circle)
.resize(100, 100)
.transform(new CircleTransform())
.into(holder.mAlbumPhotoDetailSubOwnerImage);
mDateTime = mDateTime != null ? DateUtils.getNiceTime(mDateTime) : "----";
holder.mAlbumPhotoDetailSubOwnerPostedTimeTextView.setText(mDateTime);
if(isAdded){
holder.mAddToCartButton.setText("Added TO Cart");
}else{
holder.mAddToCartButton.setText("Add TO Cart");
}
// Photo Add to cart.
holder.mAddToCartButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(InternetConnection.checkConnection(mContext)) {
new BackgroundAsyncTask(mContext, (ApiResponse) mContext, mMemberId, photo.getId(),position).execute();
} else {
DailyStudio.noInternetConnectionToast(mContext);
}
}
});
}
your Interface
public interface ApiResponse {
public void apiResponseProcessing(String response,int position);
}
Your Adapter apiResponceProcessing()
#Override
public void apiResponseProcessing(String response,int position) {
Log.i(TAG,"Api Response : "+response);
if(response.equals(Fields.JSON_SUCCESS)) {
mValues.get(position).setAddedTOCart(true);
notifyDataSetChange();
}
}
And finally your
BackgroundAsyncTask
public class BackgroundAsyncTask extends AsyncTask<Void, Void, String> {
private Context context;
private String accessToken;
private int memberId;
private int photoId;
private int mPosition;
private ApiResponse objIBaseApi;
public BackgroundAsyncTask(Context context, ApiResponse apiResponse, int memberId, int photoId,int position) {
this.context = context;
this.memberId = memberId;
this.photoId = photoId;
accessToken = MemberPreference.getAccessToken(context);
this.objIBaseApi = apiResponse;
this.mPosition = position;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(Void... params) {
JSONObject json = JSONParser.addToCartPhoto(accessToken, memberId, photoId);
if(json != null) {
Log.i(TAG,"First Json : "+json.toString());
try {
if (json.getString(Fields.RESULT).equalsIgnoreCase(Fields.JSON_SUCCESS)) {
return Fields.JSON_SUCCESS;
} else if(json.getString(Fields.JSON_ERROR).equalsIgnoreCase(Fields.ERROR_ACCESS_DENIED)) {
String refreshToken = MemberPreference.getRefreshToken(context);
JSONObject newJSONObject = JSONParser.loginMemberWithRefreshToken(refreshToken, Integer.toString(memberId));
if(newJSONObject != null) {
if(newJSONObject.getString(Fields.JSON_ERROR).equalsIgnoreCase(Fields.ERROR_ACCESS_DENIED)) {
return Fields.ERROR_ACCESS_DENIED;
} else {
return Fields.JSON_SUCCESS;
}
} else
return Fields.ERROR_ACCESS_DENIED;
} else {
return Fields.JSON_ERROR;
}
} catch (JSONException e) {
e.printStackTrace();
return Fields.JSON_ERROR;
}
}
return Fields.JSON_ERROR;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
objIBaseApi.apiResponseProcessing(result,mPosition);
}
}
Firstly in my opinion adapter should not care about network request. But
giving an answer in substance, you can try pass anonymous class for your apiResponseProcessing in same manner as you create OnClickListener for your button. It can look like this:
holder.mAddToCartButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(InternetConnection.checkConnection(mContext)) {
new BackgroundAsyncTask(
mContext,
new ApiResponse() {
#Override
public void apiResponseProcessing(String response) {
Log.i(TAG,"Api Response : "+response);
if(response.equals(Fields.JSON_SUCCESS)) {
// Here you can access you holder till it final
}
}
},
mMemberId,
photo.getId()).execute();
} else {
DailyStudio.noInternetConnectionToast(mContext);
}
}
});
But code like this looks messy and spaghetti. As i say at the beginning there are exist at least one different approach to handle changes for buttons inside listview/recivleview. I use method, where adapter only care about building interface with given data and delegate buttons clicks to someone else (in most cases activity that contains listview). An easy way notify activity about button click is Bus messaging pattern. I use Otto event library. When delegate receive notification about button click, it can initiate data changing according current task and then initiate listview reloading or partial update only required rows.
Additional comments
Try to write beautiful code. Constructor AlbumPhotoDetailAdapter has different syntax to assign instance variables. One with this keyword and other without. Usually you should use one way.
public AlbumPhotoDetailAdapter(Activity context, ArrayList<Photo> items) {
this.values = items;
this.context = context;
this.memberId = MemberPreference.getMemberId(context);
}
album instance variable have no access modifiers indication. You should know, that in java programming language omitting access specifiers is not the same as private modifier.
I'm trying to populate a layout of mine by looping through some requested JSON (I use Retrofit).
When I try to populate the layout manually (like below), it displays fine:
Post post1 = new Post("1", "1", "This is a message.");
But if I try to populate it with the requested JSON data, the layout doesn't get populated nor does it display on my screen. Only the layout with "This is a message." is displayed.
Here is the code within my onCreateView() for my fragment:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
ListView listView = (ListView) view.findViewById(R.id.listview_posts);
final ArrayList<Post> arrayOfUsers = new ArrayList<Post>();
// This works fine. It populates the layout as it should.
Post post1 = new Post("1", "1", "This is a message.");
arrayOfUsers.add(post1);
final RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(BASE_URL)
.build();
final ApiEndpointInterface apiService = restAdapter.create(ApiEndpointInterface.class);
apiService.getJsonStuff(1, new Callback<PostData>() {
#Override
public void success(PostData postData, Response response) {
// This doesn't work either
Post post2 = new Post("1", "1", "This is a message2.");
arrayOfUsers.add(post2);
for (Post p : postData.data) {
// This does not work. The layout isn't populated nor does it display.
Post posty = new Post(p.getId(), p.getUserId(), p.getContent());
arrayOfUsers.add(posty);
// The JSON is being read correctly, since this prints out the right values.
Log.d("MESSAGE", p.getMessage());
}
}
#Override
public void failure(RetrofitError retrofitError) {
retrofitError.printStackTrace();
}
});
PostAdapter adapter = new PostAdapter(getActivity(), arrayOfUsers);
listView.setAdapter(adapter);
return view;
}
The callback:
void getJsonStuff(#Path("user_id") int userId, Callback<PostData> response);
Post model:
import java.util.ArrayList;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Post {
#Expose
private String id;
#SerializedName("user_id")
#Expose
private String userId;
#Expose
private String content;
public Post(String id, String userId, String content) {
this.id = id;
this.userId = userId;
this.content = content;
}
/**
*
* #return
* The id
*/
public String getId() {
return id;
}
/**
*
* #param id
* The id
*/
public void setId(String id) {
this.id = id;
}
/**
*
* #return
* The userId
*/
public String getUserId() {
return userId;
}
/**
*
* #param userId
* The user_id
*/
public void setUserId(String userId) {
this.userId = userId;
}
/**
*
* #return
* The content
*/
public String getContent() {
return content;
}
/**
*
* #param content
* The content
*/
public void setContent(String content) {
this.content= content;
}
}
PostData model:
import java.util.ArrayList;
import java.util.List;
import com.google.gson.annotations.Expose;
public class PostData {
#Expose
public Boolean success;
#Expose
public List<Post> data = new ArrayList<Post>();
/**
*
* #return
* The success
*/
public Boolean getSuccess() {
return success;
}
/**
*
* #param success
* The success
*/
public void setSuccess(Boolean success) {
this.success = success;
}
/**
*
* #return
* The data
*/
public List<Post> getData() {
return data;
}
/**
*
* #param data
* The data
*/
public void setData(List<Post> data) {
this.data = data;
}
}
In the scenario that works for you -> you are doing the things sequentially: create the Post object - add it to the list - create the adapter based on the non-empty list - set the adapter on the list.
In the scenario that doesn't work, you are doing them asynchronously: create empty list - trigger request for data (but no data yet) - create adapter - set the adapter on the list - at some undetermined moment in the future data arrives. The problem is that in this case the adapter doesn't know that anything changed, so you need to notify it (at the end of your success callback):
adapter.notifyDataSetChanged()
Your getJsonStuff method should be declared something like...
getJsonStuff(int id, Callback<List<Post>> callback)
I am using spinner which get populated dynamically from data base
here is my code
<Spinner
android:id="#+id/spnOrdrPrdBrand"
style="#style/ButtonStyleSpinner"
android:layout_marginTop="5dp"
android:hint="#string/select"
android:paddingLeft="5dp" />
List<Brand> brandList = new ArrayList<Brand>();
if(!custId.equals("0")){
brandList = cCon.getBrandList(custId);
}
// Sorting
//Collections.sort(brandList);
//Brand Lst
ArrayAdapter<Brand> brandAdp = new ArrayAdapter<Brand>(this,android.R.layout.simple_spinner_item,brandList.toArray(new Brand[brandList.size()]));
brandAdp.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spnOrdrPrdBrand.setAdapter(brandAdp);
data is thr in brandList object
but while populating showing me object name instead actual brand name.
I am using
public class Brand implements Comparable<Brand>{
// private variables
protected int brandId;
protected String brandNm;
// Empty constructor
public Brand() {
}
// constructor
public Brand(int brandId, String brandNm) {
this.brandId = brandId;
this.brandNm = brandNm;
}
/**
* #return the brandId
*/
public int getBrandId() {
return brandId;
}
/**
* #param brandId the brandId to set
*/
public void setBrandId(int brandId) {
this.brandId = brandId;
}
/**
* #return the brandNm
*/
public String getBrandNm() {
return brandNm;
}
/**
* #param brandNm the brandNm to set
*/
public void setBrandNm(String brandNm) {
this.brandNm = brandNm;
}
#Override
public int compareTo(Brand another) {
if (this.brandNm.equals(another.brandNm)) {
return ((Integer.valueOf(this.brandId)).compareTo(Integer.valueOf(another.brandId)));
} else {
return this.brandNm.compareTo(another.brandNm);
}
}
}
so how to resolve for the same
What are you showing is the toString implementation of Object. If you do not want to have a custom adapter, you can override toString() in your Brand class and let it returns the String you want to show,