How to use a ViewModel outside of onCreate()? - android

I have an Activity with a RecyclerView which display Livedata from a room database.
My aim is to start a new Activity with more data from the room database when the user is clicking on the corresponding item in the RecyclerView.
For that I overwrote the onClick() method in the adapter of the RecylcerView. Each object of the RecyclerView has a Id, I need that Id to get the corresponding data from the database. So I passed the Id from the Adapter to the Activity.
To search an element by Id in the database that I need the ViewModel object in the MainAcitivty. It is initialized in the onCreate() of the Activity. The method I called in the Adapter is outside the onCreate() and I get a null object reference exception when I try to use it.
How can I use the ViewModel outside of the onCreate() method of the Activity? Or is there another way to search for the element in the database?
Thank you!
The Adapter class:
In the onClick() method is the relevant part.
package com.example.fillmyplate.activities;
import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.fillmyplate.R;
import com.example.fillmyplate.entitys.Recipe;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
class RecipeAdapter extends RecyclerView.Adapter<RecipeAdapter.RecipeViewHolder> {
private static final String TAG = "RecipeAdapter";
private List<Recipe> mRecipes = new ArrayList<>();
private LayoutInflater mInflater;
private Context mContext;
private MainActivity mainActivity = new MainActivity();
private static int backGroundIndex = 0;
class RecipeViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public final TextView recipeTitleItemView;
ImageView imageView;
public RecipeViewHolder(View itemView) {
super(itemView);
recipeTitleItemView = itemView.findViewById(R.id.name);
imageView = itemView.findViewById(R.id.card_image_view);
Log.d(TAG, "RecipeViewHolder: index " + backGroundIndex);
if (backGroundIndex == 0) {
imageView.setImageResource(R.drawable.background_green);
backGroundIndex++;
} else if (backGroundIndex == 1 ) {
imageView.setImageResource(R.drawable.background_red);
backGroundIndex++;
} else if (backGroundIndex == 2 ) {
imageView.setImageResource(R.drawable.background_blue);
backGroundIndex = 0;
}
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
int position = getAdapterPosition();
// This should be the mistake.
mainActivity.startKnownRecipeActivity(position);
}
}
public RecipeAdapter(Context context) {
mInflater = LayoutInflater.from(context);
this.mContext = context;
}
#NonNull
#Override
public RecipeAdapter.RecipeViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
// Inflate an item view
View mRecipeTitleView = mInflater.inflate(
R.layout.recipe_list_row, parent, false);
return new RecipeViewHolder(mRecipeTitleView);
}
// Get data into the corrsponding views
#Override
public void onBindViewHolder(RecipeAdapter.RecipeViewHolder holder, int position) {
Log.d(TAG, "onBindViewHolder: " + position);
Recipe currentRecipe = mRecipes.get(position);
Log.d(TAG, "onBindViewHolder: setText " + currentRecipe);
holder.recipeTitleItemView.setText(currentRecipe.getTitle());
}
#Override
public int getItemCount() {
return mRecipes.size();
}
public void setRecipes(List<Recipe> recipes) {
this.mRecipes = recipes;
Log.d(TAG, "setRecipes: notifydataChanged" );
notifyDataSetChanged();
}
}
MainActivity:
package com.example.fillmyplate.activities;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.media.Image;
import android.os.Build;
import android.os.Bundle;
import com.example.fillmyplate.R;
import com.example.fillmyplate.entitys.Recipe;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.cardview.widget.CardView;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainAcitivity";
private static final int NEW_RECIPE_ACTIVITY_REQUEST_CODE = 1;
private RecyclerView mRecyclerView;
private RecipeViewModel mRecipeViewmodel;
private RecyclerView.LayoutManager layoutManager;
//private final List<String> mTitleList = new LinkedList<>();
//NEU for adapter
private List<String> recipeDataList = new ArrayList<>();
RecipeRoomDatabase db;
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// RECYCLER VIEW STUFF
mRecyclerView = findViewById(R.id.recycler_view1);
mRecyclerView.setHasFixedSize(true);
// user linerar layout manager
layoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(layoutManager);
// specify an adapter
final RecipeAdapter recipeAdapter = new RecipeAdapter(this);
//mAdapter = new RecipeAdapter(this, mTitleList);
mRecyclerView.setAdapter(recipeAdapter);
mRecipeViewmodel = ViewModelProviders.of(this).get(RecipeViewModel.class);
mRecipeViewmodel.getAllRecipes().observe(this, new Observer<List<Recipe>>() {
#Override
public void onChanged(List<Recipe> recipes) {
Log.d(TAG, "onChanged: " + recipes.toString());
for (Recipe rec : recipes) {
Log.d(TAG, "onChanged: " + rec.getTitle());
Log.d(TAG, "onChanged: recipe id " + rec.getUid());
}
recipeAdapter.setRecipes(recipes);
}
});
// DB
db = Room.databaseBuilder(getApplicationContext(), RecipeRoomDatabase.class, "appdb").build();
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, AddRecipeActivity.class);
startActivityForResult(intent, NEW_RECIPE_ACTIVITY_REQUEST_CODE);
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d(TAG, "onActivityResult: ");
if (requestCode == NEW_RECIPE_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) {
Log.d(TAG, "onActivityResult: " + data.getStringExtra(AddRecipeActivity.EXTRA_REPLY));
// mTitleList.add(data.getStringExtra(AddRecipeActivity.EXTRA_REPLY));
Recipe rec = new Recipe(data.getStringExtra(AddRecipeActivity.EXTRA_REPLY));
mRecipeViewmodel.insert(rec);
} else {
Toast.makeText(
getApplicationContext(),
"saved",
Toast.LENGTH_LONG).show();
}
}
public void startKnownRecipeActivity(int position) {
Log.d(TAG, "startKnownRecipeActivity: Position " + position);
LiveData<List<Recipe>> recipe = mRecipeViewmodel.findById(position);
if (recipe.getValue().size() > 1) {
Log.d(TAG, "startKnownRecipeActivity: Error database found more than one recipe.");
} else {
Log.d(TAG, "startKnownRecipeActivity: Start activity with recipe " + recipe.getValue().get(0).getTitle());
}
}
}

The thing you need to do is to use a call back to send position back to activity.
To make sure that view position is correct you need to override 3 functions in RecyclerView Adapter:
#Override
public int getItemCount() {
return filteredUsers.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
For the Callback just create an Interface:
public interface AdapterListener {
void onClick(int id);
void onClick(ViewModel object);
}
Make a method in your Recycler Adapter:
private AdapterListener adapterListener;
public void setAdapterListener(AdapterListener mCallback) {
this.adapterListener = mCallback;
}
Implement this Interface on your Activity then you will get both methods of the interface.
public class MainActivity extends AppCompatActivity implements AdapterListener{
Register the listener by calling the setAdapterListener method in your activity after the initialization of the RecyclerView
adapterObject.setAdapterListener(MainActivity.this);
Then the last thing you need to do is call interface method in your item onClickListener, where u can either pass the complete model or just the id of the model
adapterListener.onClick(modelObject.getId());
OR
adapterListener.onClick(modelObject);

Related

Recycler View OnClickListener

I have a recycler view, that is picking the data from an URL and it stores it, but I want to set an OnClickListener so I can click on a specific field and show some extra data.
I tried to implement a ClickListener but it gives me an error.
if this way I tried is not good feel free to suggest a better way for my code.
Adapter Class:
package com.example.zlatnakopackajson1;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class PonudiAdapter extends RecyclerView.Adapter<PonudiViewHolder> {
private Context context;
ArrayList<Ponudi> ponudis;
public PonudiAdapter() {
ponudis = new ArrayList<>();
}
public void setData(ArrayList<Ponudi> ponudis) {
this.ponudis = ponudis;
}
#NonNull
#Override
public PonudiViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater layoutInflater = LayoutInflater.from(context);
View ponudiView = layoutInflater.inflate(R.layout.adapter_view_layout,parent,false);
return new PonudiViewHolder(ponudiView);
}
#Override
public void onBindViewHolder(#NonNull PonudiViewHolder holder, int position) {
final Ponudi ponudi = ponudis.get(position);
// holder.sh_sport_id.setText(ponudi.sh_sport_id);
if ( ponudi.sh_sport_id.equals("1")) {
holder.sh_sport_id.setText("Фудбал");
holder.imgSport.setImageResource(R.drawable.fudbal);
}
else if (ponudi.sh_sport_id.equals("2") ) {
holder.sh_sport_id.setText("Хокеј");
holder.imgSport.setImageResource(R.drawable.hokej);
}
else if (ponudi.sh_sport_id.equals("3") ) {
holder.sh_sport_id.setText("Кошарка");
holder.imgSport.setImageResource(R.drawable.basketball);
}
else if (ponudi.sh_sport_id.equals("4") ) {
holder.sh_sport_id.setText("Тенис");
holder.imgSport.setImageResource(R.drawable.tenis);
}
else if (ponudi.sh_sport_id.equals("5") ) {
holder.sh_sport_id.setText("Ракомет");
// holder.imgSport.setImageResource(R.drawable.handball);
}
else if (ponudi.sh_sport_id.equals("6") ) {
holder.sh_sport_id.setText("MLB");
// holder.imgSport.setImageResource(R.drawable.mlb);
}
else if (ponudi.sh_sport_id.equals("7") ) {
holder.sh_sport_id.setText("Одбојка");
holder.imgSport.setImageResource(R.drawable.odbojka);
}
else if (ponudi.sh_sport_id.equals("8") ) {
holder.sh_sport_id.setText("Рагби");
holder.imgSport.setImageResource(R.drawable.ragbi);
}
else if (ponudi.sh_sport_id.equals("9") ) {
holder.sh_sport_id.setText("Формула");
// holder.imgSport.setImageResource(R.drawable.formula);
}
else if (ponudi.sh_sport_id.equals("10") ) {
holder.sh_sport_id.setText("Мото Спорт");
// holder.imgSport.setImageResource(R.drawable.moto);
}
else if (ponudi.sh_sport_id.equals("11") ) {
holder.sh_sport_id.setText("Ватерполо");
holder.imgSport.setImageResource(R.drawable.vaterpolo);
}
else if (ponudi.sh_sport_id.equals("12") ) {
holder.sh_sport_id.setText("Бокс");
// holder.imgSport.setImageResource(R.drawable.boks);
}
else if (ponudi.sh_sport_id.equals("13") ) {
holder.sh_sport_id.setText("Футсал");
holder.imgSport.setImageResource(R.drawable.futsal);
}
else if (ponudi.sh_sport_id.equals("14") ) {
holder.sh_sport_id.setText("Пинг Понг");
// holder.imgSport.setImageResource(R.drawable.tenis);
}
else {
holder.sh_sport_id.setText("Останато");
holder.imgSport.setImageAlpha(0);
}
holder.tim1.setText(ponudi.tim1);
holder.tim2.setText(ponudi.tim2);
holder.liga_header.setText(ponudi.liga_header);
holder.parent_layout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(context,MatchActivity.class);
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return ponudis.size();
}
}
ViewHolder Class:
package com.example.zlatnakopackajson1;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
public class PonudiViewHolder extends RecyclerView.ViewHolder {
TextView sh_sport_id;
TextView tim1;
TextView tim2;
TextView liga_header;
ImageView imgSport;
LinearLayout parent_layout;
public PonudiViewHolder(#NonNull View itemView) {
super(itemView);
sh_sport_id = itemView.findViewById(R.id.textView1);
tim1 = itemView.findViewById(R.id.textView2);
tim2 = itemView.findViewById(R.id.textView3);
liga_header = itemView.findViewById(R.id.textView4);
imgSport = itemView.findViewById(R.id.imgSport);
parent_layout = itemView.findViewById(R.id.parent_layout);
}
}
Error Logcat:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
at android.content.ComponentName.<init>(ComponentName.java:131)
at android.content.Intent.<init>(Intent.java:6510)
at com.example.zlatnakopackajson1.PonudiAdapter$1.onClick(PonudiAdapter.java:114)
at android.view.View.performClick(View.java:7125)
at android.view.View.performClickInternal(View.java:7102)
at android.view.View.access$3500(View.java:801)
at android.view.View$PerformClick.run(View.java:27336)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
New Activity:
package com.example.zlatnakopackajson1;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public class MatchActivity extends AppCompatActivity {
private static final String TAG = "MatchActivity";
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_match);
Log.d(TAG, "OnCrate: started");
getIncomingIntent();
}
private void getIncomingIntent() {
Log.d(TAG, "getIncomingIntent: checking for incoming intent");
if (getIntent().hasExtra("tim1") && getIntent().hasExtra("tim2")) {
Log.d(TAG, "getIncomingIntetnt: found intent extras.");
String tim1 = getIntent().getStringExtra("tim1");
String tim2 = getIntent().getStringExtra("tim2");
setItems(tim1,tim2);
}
}
private void setItems (String tim1, String tim2) {
Log.d(TAG,"setItems: setting the tims to widgets.");
TextView Tim1 = findViewById(R.id.txtMatchT1);
Tim1.setText(tim1);
TextView Tim2 = findViewById(R.id.txtMatchT2);
Tim2.setText(tim2);
}
}
Here in your PonudiAdapter class you have a private member Context.
But you are not setting the value for the context in your class constructor.
Change your class constructor to pass a context object
public class PonudiAdapter extends RecyclerView.Adapter<PonudiViewHolder> {
private Context context;
ArrayList<Ponudi> ponudis;
public PonudiAdapter(Context context) {
ponudis = new ArrayList<>();
this.context = context;
}
...
}
Then in your class where you initialize the PonudiAdapter, pass the application context.
PonudiAdapter adapter = new PonudiAdapter(getApplicationContext());
Hope this answers your question. Please feel free to comment in case you run into any other issues.

How to add a button to Firebase Recycler view in Android?

I want to add a button for each item in the Firebase recycler view.
I've created the button, it's being showed, and i'm receiving the Toast when I press it.
But - How can I get the relevant item on it's list ?
For example - When I press the 'DELETE' I want to delete that certain mission 'aspodm'. (Sorry for the large picture, how do I make it smaller?)
This is how the database looks like (vfPvsk... is the user id, and 773f... is random uuid for mission id):
And that is the code of the relevant fragment. (The Toast when clicking the button is activated - I just don't know how to get to the relevant mission in order to delete it)
package com.airpal.yonatan.airpal;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.firebase.ui.database.FirebaseRecyclerAdapter;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.Query;
/**
* A simple {#link Fragment} subclass.
*/
public class PendingFragment_User extends Fragment {
private String TAG = "dDEBUG";
private RecyclerView mPendingList;
private DatabaseReference mMissionsDb;
private FirebaseAuth mAuth;
private String mCurrent_user_id;
private View mMainView;
Query queries;
public PendingFragment_User() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mMainView = inflater.inflate(R.layout.fragment_pending_user, container, false);
mPendingList = (RecyclerView)mMainView.findViewById(R.id.pending_recycler_user);
mAuth = FirebaseAuth.getInstance();
mCurrent_user_id = mAuth.getCurrentUser().getUid();
mMissionsDb = FirebaseDatabase.getInstance().getReference().child("Missions").child(mCurrent_user_id);
queries = mMissionsDb.orderByChild("status").equalTo("Available");
mMissionsDb.keepSynced(true);
mPendingList.setHasFixedSize(true);
mPendingList.setLayoutManager(new LinearLayoutManager(getContext()));
// Inflate the layout for this fragment
return mMainView;
}
#Override
public void onStart() {
super.onStart();
FirebaseRecyclerAdapter<Mission, PendingFragment_User.MissionsViewHolder> firebaseMissionsUserRecyclerAdapter = new FirebaseRecyclerAdapter<Mission, PendingFragment_User.MissionsViewHolder>(
Mission.class,
R.layout.missions_single_layout,
PendingFragment_User.MissionsViewHolder.class,
queries
) {
#Override
protected void populateViewHolder(PendingFragment_User.MissionsViewHolder missionViewHolder, final Mission missionModel, int missionPosition) {
// Log.d(TAG, "inside populateViewHolder" + missionModel.getType() + " , " + missionModel.getDescription());
missionViewHolder.setMissionName(missionModel.getType());
missionViewHolder.setMissionDescription(missionModel.getDescription());
missionViewHolder.setMissionStatus(missionModel.getStatus());
missionViewHolder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Toast.makeText(view.getContext(), "esto es un boton"+ view.getNextFocusUpId(), Toast.LENGTH_SHORT).show();
Log.d(TAG,"The button was pressed");
}
});
}
};
mPendingList.setAdapter(firebaseMissionsUserRecyclerAdapter);
}
public static class MissionsViewHolder extends RecyclerView.ViewHolder {
View mView;
Button button ;
public MissionsViewHolder(View itemView) {
super(itemView);
mView = itemView;
button = (Button)mView.findViewById(R.id.pending_single_button);
}
public void setMissionName(String name){
TextView mMissionNameView = mView.findViewById(R.id.mission_single_name);
mMissionNameView.setText(name);
}
public void setMissionStatus(String status){
TextView mMissionStatusView = mView.findViewById(R.id.mission_single_status);
mMissionStatusView.setText(status);
if (status.equals("Available")){
mMissionStatusView.setTextColor(Color.parseColor("#008000"));;
} else {
mMissionStatusView.setTextColor(Color.parseColor("#FF0000"));;
}
}
public void setMissionDescription(String description){
TextView mMissionDescriptionView = mView.findViewById(R.id.mission_single_description);
mMissionDescriptionView.setText(description);
}
}
}
Try using a subclass rather than anonymous one. Then, you'll be able to access the getItem(int position) method of the adapter. Something along the lines of:
private class MissionAdapter extends FirebaseRecyclerAdapter<Mission, PendingFragment_User.MissionsViewHolder> {
public MissionAdapter(Query queries){
super(Mission.class, R.layout.missions_single_layout, PendingFragment_User.MissionsViewHolder.class, queries);
}
#Override
protected void populateViewHolder(PendingFragment_User.MissionsViewHolder missionViewHolder, final Mission missionModel, final int missionPosition) {
Log.d(TAG, "inside populateViewHolder" + missionModel.getType() + " , " + missionModel.getDescription());
missionViewHolder.setMissionName(missionModel.getType());
missionViewHolder.setMissionDescription(missionModel.getDescription());
missionViewHolder.setMissionStatus(missionModel.getStatus());
missionViewHolder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Mission clickedMission = MissionAdapter.this.getItem(missionPosition);
if (clickedMission != null){ // for the sake of being extra-safe
Log.d(TAG,"The button was pressed for mission: " + clickedMission.getType());
}
}
});
}
}
I made it print a log, but you should be able to do anything you want with the retrieved Mission object in there.

E/RecyclerView: No adapter attached; skipping layout (Using Fragment)

I have been reading the different answers here on stackoverflow and on this blog post and tried to implement their solutions but I am still getting the error: No adapter attached; skipping layout.
RecyclerAdapter:
package vn.jupviec.frontend.android.monitor.app.ui.candidate;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.w3c.dom.Text;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import butterknife.BindView;
import vn.jupviec.frontend.android.monitor.app.R;
import vn.jupviec.frontend.android.monitor.app.domain.candidate.TrainingDTO;
import vn.jupviec.frontend.android.monitor.app.util.Utils;
/**
* Created by Windows 10 Gamer on 16/12/2016.
*/
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.RecyclerViewHolder> {
private static final String TAG = "RecyclerView";
private List<TrainingDTO> mTrainingDTOs;
private Context mContext;
private LayoutInflater mLayoutInflater;
private String today;
private static final DateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
AdapterInterface buttonListener;
public RecyclerAdapter(Context context, List<TrainingDTO> datas, AdapterInterface buttonListener) {
mContext = context;
mTrainingDTOs = datas;
this.mLayoutInflater = LayoutInflater.from(mContext);
this.buttonListener = buttonListener;
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.candidate_item, parent, false);
// View itemView = mLayoutInflater.inflate(R.layout.candidate_item, parent, false);
return new RecyclerViewHolder(itemView);
}
#Override
public void onBindViewHolder(RecyclerViewHolder holder, int position) {
final ObjectMapper mapper = new ObjectMapper();
final TrainingDTO trainingDTO = mapper.convertValue(mTrainingDTOs.get(position), TrainingDTO.class);
holder.tvName.setText(trainingDTO.getMaidName());
holder.status.setVisibility(View.GONE);
Date date = new Date();
String currentDate = sdf.format(date);
for (TrainingDTO.TrainingDetailDto td : trainingDTO.getListTrain()) {
today = Utils.formatDate((long) td.getTrainingDate(),"dd/MM/yyyy");
break;
}
if(currentDate.equals(today)) {
holder.status.setVisibility(View.VISIBLE);
holder.status.setText("(Mới)");
}
holder.btnHistory.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mContext,R.string.msg_no_candidate_history,Toast.LENGTH_SHORT).show();
}
});
holder.btnTest.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
buttonListener.showComment(trainingDTO);
}
});
}
#Override
public int getItemCount() {
int size ;
if(mTrainingDTOs != null && !mTrainingDTOs.isEmpty()) {
size = mTrainingDTOs.size();
}
else {
size = 0;
}
return size;
}
class RecyclerViewHolder extends RecyclerView.ViewHolder {
private TextView tvName;
private Button btnTest;
private Button btnHistory;
private TextView status;
public RecyclerViewHolder(View itemView) {
super(itemView);
tvName = (TextView) itemView.findViewById(R.id.txtName);
btnTest = (Button) itemView.findViewById(R.id.btnTest);
btnHistory = (Button) itemView.findViewById(R.id.btnHistory);
status = (TextView)itemView.findViewById(R.id.status);
}
}
public boolean removeItem(int position) {
if (mTrainingDTOs.size() >= position + 1) {
mTrainingDTOs.remove(position);
return true;
}
return false;
}
public interface AdapterInterface {
void showComment(TrainingDTO trainingDTO);
void showHistory(TrainingDTO trainingDTO);
}
}
FragmentTapNew:
package vn.jupviec.frontend.android.monitor.app.ui.candidate;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ClipDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;
import vn.jupviec.frontend.android.monitor.app.R;
import vn.jupviec.frontend.android.monitor.app.domain.ResultDTO;
import vn.jupviec.frontend.android.monitor.app.domain.candidate.TrainingDTO;
import vn.jupviec.frontend.android.monitor.app.domain.training.CandidateService;
import vn.jupviec.frontend.android.monitor.app.util.Constants;
import vn.jupviec.frontend.android.monitor.app.util.Utils;
/**
* Created by Windows 10 Gamer on 09/12/2016.
*/
public class FragmentTapNew extends Fragment implements RecyclerAdapter.AdapterInterface {
private static final String TAG = FragmentTapNew.class.getSimpleName();
Activity myContext = null;
private OnItemSelectedListener listener;
ShapeDrawable shapeDrawable;
#BindView(R.id.lvToday)
RecyclerView lvToday;
#BindView(R.id.textView)
TextView textView;
#BindView(R.id.pb_loading)
ProgressBar pbLoading;
private Unbinder unbinder;
private boolean loading = true;
int pastVisiblesItems, visibleItemCount, totalItemCount;
ArrayList<TrainingDTO> mTrainingDTO ;
RecyclerAdapter mTrainingDTOAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.activity_candidate_training, container, false);
unbinder = ButterKnife.bind(this, v);
initViews();
return v;
}
private void initViews() {
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
lvToday.setHasFixedSize(true);
lvToday.setNestedScrollingEnabled(false);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
today();
}
});
t.start();
}
private boolean isNetworkConnected() {
ConnectivityManager cm = (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
if (ni == null) {
Toast.makeText(getActivity(), R.string.err_cannot_establish_connection, Toast.LENGTH_SHORT).show();
return false;
} else
return true;
}
private void today() {
if(isNetworkConnected()){
String token = "a";
Integer date = 0;
setLoadingVisible(true);
CandidateService.getApiDummyClient(getContext()).getCandidate(
token,
date,
Constants.TRAINING,
Arrays.asList(Constants.TRAINING_DAY_NEW),
new Callback<ResultDTO>() {
#Override
public void success(ResultDTO resultDTO, Response response) {
setLoadingVisible(false);
mTrainingDTO = (ArrayList<TrainingDTO>) resultDTO.getData();
if (mTrainingDTO.size() == 0) {
if(textView!=null) {
textView.setVisibility(View.VISIBLE);
textView.setTextColor(Color.RED);
textView.setGravity(Gravity.CENTER | Gravity.BOTTOM);
}
} else {
if(textView!=null) {
textView.setVisibility(View.GONE);
}
if(null==mTrainingDTOAdapter) {
mTrainingDTOAdapter = new RecyclerAdapter(getActivity(), mTrainingDTO, FragmentTapNew.this);
lvToday.setAdapter(mTrainingDTOAdapter);
} else {
lvToday.setAdapter(mTrainingDTOAdapter);
mTrainingDTOAdapter.notifyDataSetChanged();
}
lvToday.invalidate();
}
}
#Override
public void failure(RetrofitError error) {
Log.e(TAG, "JV-ERROR: " + error.getMessage());
Log.e(TAG, "JV-ERROR: " + error.getSuccessType());
}
});
}
}
#Override
public void onResume() {
setLoadingVisible(false);
initViews();
lvToday.setAdapter(mTrainingDTOAdapter);
super.onResume();
}
#Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
private void setLoadingVisible(boolean visible) {
// pbLoading.getIndeterminateDrawable().setColorFilter(0xFFFF0000, android.graphics.PorterDuff.Mode.MULTIPLY);
if(pbLoading!=null) {
pbLoading.setVisibility(visible ? View.VISIBLE : View.GONE);
lvToday.setVisibility(visible ? View.GONE : View.VISIBLE);
}
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof OnItemSelectedListener) {
listener = (OnItemSelectedListener) activity;
} else {
throw new ClassCastException(activity.toString()
+ " phải implemenet MyListFragment.OnItemSelectedListener");
}
}
#Override
public void onDetach() {
super.onDetach();
listener = null;
}
#Override
public void showComment(TrainingDTO trainingDTO) {
Intent intent = new Intent(getContext(), CommentActivity.class);
intent.putExtra(Constants.ARG_NAME_DETAIL, trainingDTO);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getContext().startActivity(intent);
}
#Override
public void showHistory(TrainingDTO trainingDTO) {
Intent intent = new Intent(getContext(), HistoryActivity.class);
intent.putExtra(Constants.ARG_CANDIDATE_HISTORY_DETAIL, trainingDTO);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getContext().startActivity(intent);
}
public interface OnItemSelectedListener {
void showComment(TrainingDTO trainingDTO);
void showHistory(TrainingDTO trainingDTO);
}
}
The first, you don't need to set adapter multiple time, only set adapter at the first time in your method initView().
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
lvToday.setHasFixedSize(true);
lvToday.setNestedScrollingEnabled(false);
if(mTrainingDTO == null) {
mTrainingDTO = new ArrayList<>();
}
if(null == mTrainingDTOAdapter ) {
mTrainingDTOAdapter = new RecyclerAdapter(getActivity(), mTrainingDTO, FragmentTapNew.this);
}
lvToday.setAdapter(mTrainingDTOAdapter);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
today();
}
});
t.start();
After that, you don't need to call lvToday.setAdapter(mTrainingDTOAdapter);
many times. Just call mTrainingDTOAdapter.notifyDataSetChanged(); if having any changes in your data mTrainingDTO.
The errorlog - error:No adapter attached; skipping layout indicates that you've attached LayoutManager but you haven't attached an adapter yet, so recyclerview will skip layout. I'm not sure but, in other words recyclerView will skip layout measuring at the moment.
to prevent this errorLog Try to set an adapter with an empty dataSet i.e. ArrayList or
Attach LayoutManager just before you are setting your adapter.
Try this,
private void initViews() {
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
LinearLayoutManager mLinearLayoutManager = new LinearLayoutManager(getActivity());
lvToday.setLayoutManager(mLinearLayoutManager);
lvToday.setHasFixedSize(true);
lvToday.setNestedScrollingEnabled(false);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
today();
}
});
t.start();
}

Bug in app while rotating Android phone

hello guys i started to learn to build android applications via udacy. After a long troubleshooting i didnt find something that can cause the bug that i have. the bug is that when i rotate the phone the view seems to clone it self and not deleting the previous one if someone can help me ill be very happy.
1) normal state of app:
2) after rotation:
3) and after another rotation the application crashing
it never happend until i added the code in the xml file of the view:
<?xml version="1.0" encoding="utf-8"?>
<!-- Layout for weather forecast list item for future day (not today) -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:id="#+id/list_item_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#mipmap/ic_launcher"/>
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:orientation="vertical"
android:paddingLeft="16dp">
<TextView
android:id="#+id/list_item_date_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tomorrow"/>
<TextView
android:id="#+id/list_item_forecast_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Clear"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="1"
android:gravity="right">
<TextView
android:id="#+id/list_item_high_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="81"
android:paddingLeft="10dp"/>
<TextView
android:id="#+id/list_item_low_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="68"
android:paddingLeft="10dp"/>
</LinearLayout>
</LinearLayout>
Activity Code:
package com.example.andy.sunshine.app;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends AppCompatActivity {
private String FORECASTFRAGMENT_TAG = "FFTAG";
String mLocation;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment, new MainActivityFragment(), FORECASTFRAGMENT_TAG)
.commit();
}
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mLocation = Utility.getPreferredLocation(this);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
#Override
protected void onResume() {
super.onResume();
String location = Utility.getPreferredLocation(this);
if(location != null && location != mLocation){
MainActivityFragment ff = (MainActivityFragment)getSupportFragmentManager().findFragmentById(R.id.fragment);
if(ff != null){
ff.onLocationChanged();
}
mLocation = location;
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if(id == R.id.action_settings){
Intent settings = new Intent(this,SettingsActivity.class);
startActivity(settings);
}
if(R.id.action_map == id){
openPreferedLocationMap();
}
return super.onOptionsItemSelected(item);
}
private void openPreferedLocationMap(){
String location = Utility.getPreferredLocation(this);
Uri geoLocation = Uri.parse("geo:0,0?").buildUpon().appendQueryParameter("q",location).build();
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(geoLocation);
if(intent.resolveActivity(getPackageManager()) != null){
startActivity(intent);
}
else{
Log.d("SHOWING MAP PROBLEMM","could't call"+ location+ "intent");
}
}
}
Fragment Code:
package com.example.andy.sunshine.app;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.text.format.Time;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.example.andy.sunshine.app.data.WeatherContract;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* A placeholder fragment containing a simple view.
*/
public class MainActivityFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
private static final int FORECAST_LOADER = 0;
// For the forecast view we're showing only a small subset of the stored data.
// Specify the columns we need.
private static final String[] FORECAST_COLUMNS = {
// In this case the id needs to be fully qualified with a table name, since
// the content provider joins the location & weather tables in the background
// (both have an _id column)
// On the one hand, that's annoying. On the other, you can search the weather table
// using the location set by the user, which is only in the Location table.
// So the convenience is worth it.
WeatherContract.WeatherEntry.TABLE_NAME + "." + WeatherContract.WeatherEntry._ID,
WeatherContract.WeatherEntry.COLUMN_DATE,
WeatherContract.WeatherEntry.COLUMN_SHORT_DESC,
WeatherContract.WeatherEntry.COLUMN_MAX_TEMP,
WeatherContract.WeatherEntry.COLUMN_MIN_TEMP,
WeatherContract.LocationEntry.COLUMN_LOCATION_SETTING,
WeatherContract.WeatherEntry.COLUMN_WEATHER_ID,
WeatherContract.LocationEntry.COLUMN_COORD_LAT,
WeatherContract.LocationEntry.COLUMN_COORD_LONG
};
// These indices are tied to FORECAST_COLUMNS. If FORECAST_COLUMNS changes, these
// must change.
static final int COL_WEATHER_ID = 0;
static final int COL_WEATHER_DATE = 1;
static final int COL_WEATHER_DESC = 2;
static final int COL_WEATHER_MAX_TEMP = 3;
static final int COL_WEATHER_MIN_TEMP = 4;
static final int COL_LOCATION_SETTING = 5;
static final int COL_WEATHER_CONDITION_ID = 6;
static final int COL_COORD_LAT = 7;
static final int COL_COORD_LONG = 8;
private ForecastAdapter mForecastAdapter;
public MainActivityFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Add this line in order for this fragment to handle menu events.
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.forecastfragment, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_refresh) {
updateWeather();
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// The CursorAdapter will take data from our cursor and populate the ListView.
mForecastAdapter = new ForecastAdapter(getActivity(), null, 0);
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
// Get a reference to the ListView, and attach this adapter to it.
ListView listView = (ListView) rootView.findViewById(R.id.listview_forecast);
listView.setAdapter(mForecastAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView adapterView, View view, int position, long l) {
// CursorAdapter returns a cursor at the correct position for getItem(), or null
// if it cannot seek to that position.
Cursor cursor = (Cursor) adapterView.getItemAtPosition(position);
if (cursor != null) {
String locationSetting = Utility.getPreferredLocation(getActivity());
Intent intent = new Intent(getActivity(), DetailActivity.class)
.setData(WeatherContract.WeatherEntry.buildWeatherLocationWithDate(
locationSetting, cursor.getLong(COL_WEATHER_DATE)
));
startActivity(intent);
}
}
});
return rootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
getLoaderManager().initLoader(FORECAST_LOADER, null, this);
super.onActivityCreated(savedInstanceState);
}
private void updateWeather() {
FetchWeatherTask weatherTask = new FetchWeatherTask(getActivity());
String location = Utility.getPreferredLocation(getActivity());
weatherTask.execute(location);
}
public void onLocationChanged(){
updateWeather();
getLoaderManager().restartLoader(FORECAST_LOADER,null,this);
}
#Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
String locationSetting = Utility.getPreferredLocation(getActivity());
// Sort order: Ascending, by date.
String sortOrder = WeatherContract.WeatherEntry.COLUMN_DATE + " ASC";
Uri weatherForLocationUri = WeatherContract.WeatherEntry.buildWeatherLocationWithStartDate(
locationSetting, System.currentTimeMillis());
return new CursorLoader(getActivity(),
weatherForLocationUri,
FORECAST_COLUMNS,
null,
null,
sortOrder);
}
#Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
mForecastAdapter.swapCursor(cursor);
}
#Override
public void onLoaderReset(Loader<Cursor> cursorLoader) {
mForecastAdapter.swapCursor(null);
}
}
And Class That Extends CursorAdapter "ForecastAdapter":
package com.example.andy.sunshine.app;
import android.content.Context;
import android.database.Cursor;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.andy.sunshine.app.data.WeatherContract;
/**
* {#link ForecastAdapter} exposes a list of weather forecasts
* from a {#link android.database.Cursor} to a {#link android.widget.ListView}.
*/
public class ForecastAdapter extends CursorAdapter {
private final int VIEW_TYPE_TODAY = 0;
private final int VIEW_TYPE_FUTURE_DAY = 1;
public ForecastAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
}
#Override
public int getItemViewType(int position) {
return (position == 0)? VIEW_TYPE_TODAY:VIEW_TYPE_FUTURE_DAY;
}
#Override
public int getViewTypeCount() {
return 2;
}
/**
* Prepare the weather high/lows for presentation.
*/
private String formatHighLows(Context context,double high, double low) {
boolean isMetric = Utility.isMetric(mContext);
String highLowStr = Utility.formatTemperature(context,high, isMetric) + "/" + Utility.formatTemperature(context,low, isMetric);
return highLowStr;
}
/*
This is ported from FetchWeatherTask --- but now we go straight from the cursor to the
string.
*/
private String convertCursorRowToUXFormat(Context context,Cursor cursor) {
// get row indices for our cursor
String highAndLow = formatHighLows(context,
cursor.getDouble(MainActivityFragment.COL_WEATHER_MAX_TEMP),
cursor.getDouble(MainActivityFragment.COL_WEATHER_MAX_TEMP));
return Utility.formatDate(cursor.getLong(MainActivityFragment.COL_WEATHER_DATE)) +
" - " + cursor.getString(MainActivityFragment.COL_WEATHER_DESC) +
" - " + highAndLow;
}
/*
Remember that these views are reused as needed.
*/
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
int VIEW_TYPE = getItemViewType(cursor.getPosition());
int layoutId = -1;
if(VIEW_TYPE == VIEW_TYPE_TODAY){
layoutId = R.layout.list_item_forecast_today;
}else if(VIEW_TYPE == VIEW_TYPE_FUTURE_DAY){
layoutId = R.layout.list_item_forecast;
}
View view = LayoutInflater.from(context).inflate(layoutId, parent, false);
MyViewHolder viewHolder = new MyViewHolder(view);
view.setTag(viewHolder);
return view;
}
/*
This is where we fill-in the views with the contents of the cursor.
*/
#Override
public void bindView(View view, Context context, Cursor cursor) {
// our view is pretty simple here --- just a text view
// we'll keep the UI functional with a simple (and slow!) binding.
MyViewHolder viewHolder = (MyViewHolder)view.getTag();
viewHolder.iconView.setImageResource(R.mipmap.ic_launcher);
// TODO Read date from cursor
long dateInMillis = cursor.getLong(MainActivityFragment.COL_WEATHER_DATE);
viewHolder.dateView.setText(Utility.getFriendlyDayString(context, dateInMillis));
// TODO Read weather forecast from cursor
viewHolder.descriptionView.setText(cursor.getString(MainActivityFragment.COL_WEATHER_DESC));
// Read user preference for metric or imperial temperature units
boolean isMetric = Utility.isMetric(context);
// Read high temperature from cursor
double high = cursor.getDouble(MainActivityFragment.COL_WEATHER_MAX_TEMP);
viewHolder.highTempView.setText(Utility.formatTemperature(context,high, isMetric));
// TODO Read low temperature from cursor
double low = cursor.getDouble(MainActivityFragment.COL_WEATHER_MIN_TEMP);
viewHolder.lowTempView.setText(Utility.formatTemperature(context,low, isMetric));
}
public static class MyViewHolder {
public final ImageView iconView;
public final TextView dateView;
public final TextView descriptionView;
public final TextView highTempView;
public final TextView lowTempView;
public MyViewHolder(View view){
iconView = (ImageView) view.findViewById(R.id.list_item_icon);
dateView = (TextView)view.findViewById(R.id.list_item_date_textview);
descriptionView = (TextView)view.findViewById(R.id.list_item_forecast_textview);
highTempView = (TextView)view.findViewById(R.id.list_item_high_textview);
lowTempView = (TextView)view.findViewById(R.id.list_item_low_textview);
}
}
}
if you want to see the whole project and check maybe for errors that may cause that i posting here my github https://github.com/obruchkov/Sunshine with all the code that i did. please help me guys

change the items in a ListView from another fragment

I have two fragments, lets call them Fragment A and Fragment B, which are a part of a NavigationDrawer (this is the activity they a bound to). In Fragment A I have a button. When this button is pressed, I would like another item added to the ListView in Fragment B.
What is the best way to do this? Use Intents, SavedPreferences, making something public(?) or something else?
EDIT 5: 20/7/13 This is with srains latest code
This is the NavigationDrawer that I use to start the fragments:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class Navigation_Drawer extends FragmentActivity {
public DrawerLayout mDrawerLayout; // Creates a DrawerLayout called_.
public ListView mDrawerList;
public ActionBarDrawerToggle mDrawerToggle;
private CharSequence mDrawerTitle;
private CharSequence mTitle;
private String[] mNoterActivities; // This creates a string array called _.
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
// Just setting up the navigation drawer
} // End of onCreate
// Removed the menu
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
selectItem(position);
}
}
private void selectItem(int position) {
FragmentManager fragmentManager = getSupportFragmentManager();
if (position == 0) {
Fragment qnfragment = new QuickNoteFragment();
((FragmentBase) qnfragment).setContext(this);
Bundle args = new Bundle(); // Creates a bundle called args
args.putInt(QuickNoteFragment.ARG_nOTERACTIVITY_NUMBER, position);
qnfragment.setArguments(args);
fragmentManager.beginTransaction()
.replace(R.id.content_frame, qnfragment).commit();
} else if (position == 3) {
Fragment pendViewPager = new PendViewPager(); // This is a ViewPager that includes HistoryFragment
((FragmentBase) pendViewPager).setContext(this);
Bundle args = new Bundle();
pendViewPager.setArguments(args);
fragmentManager.beginTransaction()
.replace(R.id.content_frame, pendViewPager).commit();
}
// Update title etc
}
#Override
protected void onPostCreate(Bundle savedInstanceState) { // Used for the NavDrawer toggle
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
mDrawerToggle.syncState();
}
#Override
public void onConfigurationChanged(Configuration newConfig) { // Used for the NavDrawer toggle
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggles
mDrawerToggle.onConfigurationChanged(newConfig);
}
}
This is QuickNoteFragment:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class QuickNoteFragment extends FragmentBase implements OnClickListener {
public static final String ARG_nOTERACTIVITY_NUMBER = "noter_activity";
Button b_create;
// removed other defining stuff
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.quicknote, container, false);
int i = getArguments().getInt(ARG_nOTERACTIVITY_NUMBER);
String noter_activity = getResources().getStringArray(
R.array.noter_array)[i];
FragmentManager fm = getFragmentManager();
setRetainInstance(true);
b_create = (Button) rootView.findViewById(R.id.qn_b_create);
// Removed other stuff
getActivity().setTitle(noter_activity);
return rootView;
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.qn_b_create:
String data = "String data";
DataModel.getInstance().addItem(data);
break;
}
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// TODO Auto-generated method stub
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
}
}
public interface OnItemAddedHandler { // Srains code
public void onItemAdded(Object data);
}
}
This is HistoryFragment (Remember it is part of a ViewPager):
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.RiThBo.noter.QuickNoteFragment.OnItemAddedHandler;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
public class HistoryFragment extends FragmentBase implements OnItemAddedHandler {
ListView lv;
List<Map<String, String>> planetsList = new ArrayList<Map<String, String>>();
SimpleAdapter simpleAdpt;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.history, container, false);
initList();
ListView lv = (ListView) view.findViewById(R.id.listView);
simpleAdpt = new SimpleAdapter(getActivity(), planetsList,
android.R.layout.simple_list_item_1, new String[] { "planet" },
new int[] { android.R.id.text1 });
lv.setAdapter(simpleAdpt);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parentAdapter, View view,
int position, long id) {
// We know the View is a TextView so we can cast it
TextView clickedView = (TextView) view;
Toast.makeText(
getActivity(),
"Item with id [" + id + "] - Position [" + position
+ "] - Planet [" + clickedView.getText() + "]",
Toast.LENGTH_SHORT).show();
}
});
registerForContextMenu(lv);
return view;
}
private void initList() {
// We populate the planets
planetsList.add(createPlanet("planet", "Mercury"));
planetsList.add(createPlanet("planet", "Venus"));
planetsList.add(createPlanet("planet", "Mars"));
planetsList.add(createPlanet("planet", "Jupiter"));
planetsList.add(createPlanet("planet", "Saturn"));
planetsList.add(createPlanet("planet", "Uranus"));
planetsList.add(createPlanet("planet", "Neptune"));
}
private HashMap<String, String> createPlanet(String key, String name) {
HashMap<String, String> planet = new HashMap<String, String>();
planet.put(key, name);
return planet;
}
// We want to create a context Menu when the user long click on an item
#Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
AdapterContextMenuInfo aInfo = (AdapterContextMenuInfo) menuInfo;
// We know that each row in the adapter is a Map
HashMap map = (HashMap) simpleAdpt.getItem(aInfo.position);
menu.setHeaderTitle("Options for " + map.get("planet"));
menu.add(1, 1, 1, "Details");
menu.add(1, 2, 2, "Delete");
}
#Override
public void onItemAdded(Object data) {
// to add item
String string = String.valueOf(data);
Toast.makeText(getContext(), "Data: " + string, Toast.LENGTH_SHORT).show();
planetsList.add(createPlanet("planet", string));
}
#Override
public void onStart() {
super.onStart();
DataModel.getInstance().setOnItemAddedHandler(this);
}
}
This is FragmentBase:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
public class FragmentBase extends Fragment {
private FragmentActivity mActivity; // I changed it to FragmentActivity because Activity was not working, and my `NavDrawer` is a FragmentActivity.
public void setContext(FragmentActivity activity) {
mActivity = mActivity;
}
public FragmentActivity getContext() {
return mActivity;
}
}
This is DataModel:
import android.util.Log;
import com.xxx.xxx.QuickNoteFragment.OnItemAddedHandler;
public class DataModel {
private static DataModel instance;
private static OnItemAddedHandler mOnItemAddHandler;
public static DataModel getInstance() {
if (null == instance) {
instance = new DataModel();
}
return instance;
}
public void setOnItemAddedHandler(OnItemAddedHandler handler) {
mOnItemAddHandler = handler;
}
public void addItem(Object data) {
if (null != mOnItemAddHandler)
mOnItemAddHandler.onItemAdded(data);
else {
Log.i("is context null?", "yes!");
}
}
}
Thank you
I suggest you to use interface and MVC, that will make your code much more maintainable.
First you need an interface:
public interface OnItemAddedHandler {
public void onItemAdded(Object data);
}
Then, you will need a data model:
public class DataModel {
private static DataModel instance;
private static OnItemAddedHandler mOnItemAddHandler;
public static DataModel getInstance() {
if (null == instance) {
instance = new DataModel();
}
return instance;
}
public void setOnItemAddedHandler(OnItemAddedHandler handler) {
mOnItemAddHandler = handler;
}
public void addItem(Object data) {
if (null != mOnItemAddHandler)
mOnItemAddHandler.onItemAdded(data);
}
}
When you click the button, you can add data into the datamodel:
Object data = null;
DataModel.getInstance().addItem(data);
Then, the FragmentB implements the interface OnItemAddedHandler to add item
public class FragmentB implements OnItemAddedHandler {
#Override
public void onItemAdded(Object data) {
// to add item
}
}
also, When the FragmentB start, you should register itself to DataModel:
public class FragmentB implements OnItemAddedHandler {
#Override
public void onItemAdded(Object data) {
// to add item
}
#Override
protected void onStart() {
super.onStart();
DataModel.getInstance().setOnItemAddedHandler(this);
}
}
You also can add DataModel.getInstance().setOnItemAddedHandler(this); to the onCreate method of FragmentB
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DataModel.getInstance().setOnItemAddedHandler(this);
// do other things
}
update
you can send string simply:
String data = "some string";
DataModel.getInstance().addItem(data);
then on FragementB
public class FragmentB implements OnItemAddedHandler {
#Override
public void onItemAdded(Object data) {
// get what you send into method DataModel.getInstance().addItem(data);
String string = String.valueOf(data);
}
}
update
OK. You have add DataModel.getInstance().setOnItemAddedHandler(this) in onCreateView method, so there is no need to add it in onStart() method. You can remove the whole onStart method.
onStart will be called on the fragment start, we do not need to call it in onCreateView. And on onItemAdded will be call when call the method DataModel.getInstance().addItem(data), we do not need to call it in onCreateView neither.
so, you can remove the code below from onCreateView method:
DataModel.getInstance().setOnItemAddedHandler(this);
// remove the methods below
// onItemAdded(getView());
// onStart();
You have another fragment where there is a button, you can add the codes below in the clickhandler function:
String data = "some string";
DataModel.getInstance().addItem(data);
update3
I think the HistoryFragment has been detached after you when to QuickNoteFragment
You can add code to HistoryFragment to check:
#Override
public void onDetach() {
super.onDetach();
Log.i("test", String.format("onDetach! %s", getActivity() == null));
}
update4
I think HistoryFragment and QuickNoteFragment should has an parent class, named FragmentBase:
public class FragmentBase extends Fragment {
private Activity mActivity;
public void setContext(Activity activity) {
mActivity = mActivity;
}
public Activity getContext() {
return mActivity;
}
}
HistoryFragment and QuickNoteFragment extends FragmentBase. Then when you switch between them, you can call setContext to set a Activity, like:
private void selectItem(int position) {
FragmentManager fragmentManager = getSupportFragmentManager();
if (position == 0) {
Fragment qnfragment = new QuickNoteFragment();
qnfragment.setContext(this);
// ...
} else if (position == 1) {
Fragment pagerFragment = new RemViewPager();
pagerFragment.setContext(this);
// ...
}
}
now, we can get a non-null activity in HistoryFragment by calling getContext, so we can change onItemAdded method to:
#Override
public void onItemAdded(Object data) {
// to add item
String string = String.valueOf(data);
Toast.makeText(getContext(), "Data: " + string, Toast.LENGTH_SHORT).show();
planetsList.add(createPlanet("planet", string));
}
I hope this would work.
Some good design principals:
An activity can know everything pubic about any Fragment it contains.
A Fragment should not know anything about the specific Activities that contain it.
A Fragment should NEVER know about other fragments that may or may not be contained in the Parent activity.
A suggested approach (informal design pattern) based on these principles.
Each fragment should declare an interface to be implemented by its parent activity:
public class MyFragment extends Fragment
{
public interface Parent
{
void onMyFragmentSomeAction();
}
private Parent mParent;
public onAttach(Activity activity)
{
mParent = (Parent) activity;
}
// This would actually be in a listener. Simplifying to save typing.
void onSomeButtonClick(View button)
{
mParent.onMyFragmentSomeAction();
}
}
And the activity should implement the appropriate interfaces for all of its contained fragments.
public class MyActivity extends Activity
implements MyFragment.Parent,
YourFragment.Parent,
HisFragment.Parent
{
[usual Activity code]
void onMyFragmentSomeAction()
{
if yourFragment is showing
{
yourFragment.reactToSomeAction();
}
if hisFragment is showing
{
hisFragment.observeThatSomeActionHappened();
}
[etc]
}
The broadcast approach is good, too, but it's pretty heavyweight and it requires the target Fragment to know what broadcasts will be sent by the source Fragment.
Use Broadcast. Send a boradcast from A, and B will receive and handle it.
Add a public method to B, for example, public void addListItem(), which will add data to listview in B. Fragment A try to find the instance of Fragment B using FragmentMananger.findFragmentByTag() and invoke this method.

Categories

Resources