Android AlertDialog does not show Adapter names - java

I made a AllertDialog that's supposed to display a list of apps, when I click on one of the alerdialog members it does return the right String from the ArrayAdapter.
Here's how it looks.
public class AppList {
private Activity main_activity;
private AlertDialog app_list_dialog;
private ArrayList<RemoteAppDetail> remoteAppDetails;
public AppList(Activity activity){
this.main_activity = activity;
// init();
}
/**
* Initialize the Dialog, this needs to be called before .show()/.hide().dispose();
*/
public void init(){
this.remoteAppDetails = ConnectionHandler.getLibrary();
AlertDialog.Builder app_list_builder = new AlertDialog.Builder(main_activity);
app_list_builder.setIcon(R.drawable.ic_input_add);
app_list_builder.setTitle("Installable/Updatable Apps: ");
final ArrayAdapter<String> app_list = new ArrayAdapter<String>(main_activity, R.layout.simple_list_item_single_choice);
final ArrayList<String> app_name_list = getInstallableApps();
app_list.addAll(app_name_list);
if(!app_list.isEmpty()){
//Add the app_list adapter (The button list)
app_list_builder.setAdapter(app_list, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Log.i("DIALOG SELECTED", app_list.getItem(which));
}
});
}else{
app_list_builder.setMessage("There are no apps available for download/update.");
}
//Close Button AppList
app_list_builder.setNegativeButton("Close",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
this.app_list_dialog = app_list_builder.create();
}
/**
* Show the list.
*/
public void show(){
if(this.app_list_dialog != null){
app_list_dialog.show();
}
}
/**
* Hide the list.
*/
public void hide(){
if(this.app_list_dialog != null){
app_list_dialog.hide();
}
}
/**
* Disposes the list, it will need to be reinitialized of you change your mind after disposing of the list.
*/
public void dispose(){
if(this.app_list_dialog != null){
app_list_dialog.dismiss();
}
}
/**
* Gets a list of apps that can be installed
* #return Arraylist installable apps
*/
private ArrayList<String> getInstallableApps(){
ArrayList<String> installable_apps_list = new ArrayList<String>();
if(this.remoteAppDetails != null){
for(RemoteAppDetail rea : this.remoteAppDetails){
BasicNameValuePair remote_app_info = new BasicNameValuePair(rea.filename, String.valueOf(rea.version));
BasicNameValuePair local_app_info = getLocalAppInfo(rea.filename);
if(local_app_info != null){
if(isRemoteVersionNewer(remote_app_info, local_app_info)){
installable_apps_list.add(rea.filename);
}
}else{
if(rea.unlock_status == 0){
installable_apps_list.add(rea.filename);
}
}
}
}
return installable_apps_list;
}
/**
* Check if the app exists locally.
* #param app_name_remote
* #return Local app info as BasicNameValuePair
*/
private BasicNameValuePair getLocalAppInfo(String app_name_remote){
List<PackageInfo> packs = main_activity.getPackageManager().getInstalledPackages(0);
for(PackageInfo pi : packs){
String app_name_local = pi.applicationInfo.loadLabel(main_activity.getPackageManager()).toString();
String app_version = String.valueOf(pi.versionCode);
if(app_name_remote == app_name_local){
return new BasicNameValuePair(app_name_local, app_version);
}
}
return null;
}
/**
* Compare local and remote app info.
* #param remote_app_info
* #param local_app_info
* #return Return whether or not the remote version is newer as Boolean.
*/
private boolean isRemoteVersionNewer(BasicNameValuePair remote_app_info, BasicNameValuePair local_app_info){
if(remote_app_info.getName().contains(local_app_info.getName())){
if(remote_app_info.getValue() == local_app_info.getValue()){
return true;
}
}
return false;
}
So I would really like to know what I did wrong and if this is enough information.
UPDATE 1:
This is how I call this class:
Applist main_app_list = new Applist(this);
main_app_list.init();
main_app_list.show();
and it's called like that in my main activity's onCreate method.
Someone suggested my getInstallableApps() might return empty value's,
However if you inspect the code carefully this is not a possibility. Because if they there were empty it wouldn't even have any adapter members.
Either way I added some test value's, and it remains to do the exact same thing:
final ArrayAdapter<String> app_list = new ArrayAdapter<String>(main_activity, R.layout.simple_list_item_single_choice);
final ArrayList<String> app_name_list = getInstallableApps();
// app_list.addAll(app_name_list);
app_list.add("TEST");
app_list.add("TEST2");
The rest of the code remains the same.
UPDATE 2:
When I touch one of the members, it show's their name for a brief second, or when I hold it down it show's them for as long as I hold it.

Debug your getInstallableApps() method, you will see that it's entering the for statement because there are two elements in this.remoteAppDetails, but inside wether the elements you take data from are not called properly or empty (remote_app_info,local_app_info), or the filename field is empty. Once you find whats is placing empty values to you array you can fix it.

So I just ended up forcing the color to fix this since a custom layout was to much hassle for just this.
app_list = new ArrayAdapter<String>(main_activity, R.layout.simple_list_item_single_choice){
public View getView(int position, View convertView, android.view.ViewGroup parent) {
TextView textView = (TextView) super.getView(position, convertView, parent);
textView.setTextColor(Color.BLACK);
return textView;
}
};
For future reference when you think it doesn't show adapter names, just hold down on one of the members to see if it show's the member name, if it does you know you got a font problem.

Related

How to retain the state of a recyclerview of list of custom objects?

Main Goal:-
I have a list of sports news. Each item contains a sport name and some info. Clicking on it will show the latest news regarding that particular sport. The user has the option to swipe to dismiss a news, if they don't want it in the list or they can also drag and drop it, for example, if they want to see some news on top of others.
Each item in the list is represented programmatically as a Sport.java object.
I want to retain the state of the list upon device orientation changes.
What I've tried:-
For the list, I have an arraylist of sport objects (ArrayList). I learned that to save a list of custom objects, they objects themselves need to be Parcelable. For this, I implemented the Parcelable.java interface like this:
package com.example.android.materialme;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
/**
* Data model for each row of the RecyclerView.
*/
class Sport implements Parcelable {
//Member variables representing the title and information about the sport
private String title;
private String info;
private String detail;
private final int imageResource;
/**
* Constructor for the Sport data model
* #param title The name if the sport.
* #param info Information about the sport.
*/
Sport(String title, String info, String detail, int imageResource) {
this.title = title;
this.info = info;
this.detail = detail;
this.imageResource = imageResource;
}
protected Sport(#NonNull Parcel in) {
title = in.readString();
info = in.readString();
detail = in.readString();
imageResource = in.readInt();
}
public static final Creator<Sport> CREATOR = new Creator<Sport>() {
#Override
public Sport createFromParcel(Parcel in) {
return new Sport(in);
}
#Override
public Sport[] newArray(int size) {
return new Sport[size];
}
};
String getTitle() {
return title;
}
String getInfo() {
return info;
}
int getImageResource(){
return imageResource;
}
String getDetail(){
return detail;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(title);
parcel.writeString(info);
parcel.writeString(detail);
parcel.writeInt(imageResource);
}
}
and then I used
outState.putParcelableArrayList(KEY, sportsList);
but, this doesn't work. The screen is just blank upon rotating device.
I tried debugging the app and found that the arraylist was being passed correctly with the data intact, it's just that the app is not being able to display it for some reason.
Also, the implementation of the fab button is so that it resets the whole list to its initial condition upon click. The fab works normally but if the orientation is changed once, it stops working (app doesn't crash). Changing the orientation back also doesn't fix the fab. So, to get the list again for any other test, I have to rerun the entire app.
Complete Code:-
MainActivity.java
package com.example.android.materialme;
import android.content.res.TypedArray;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;
import java.util.Collections;
public class MainActivity extends AppCompatActivity {
//Member variables
private RecyclerView mRecyclerView;
private ArrayList<Sport> mSportsData;
private SportsAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(view -> resetSports());
//Initialize the RecyclerView
mRecyclerView = (RecyclerView)findViewById(R.id.recyclerView);
//Set the Layout Manager
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
//Initialize the ArrayLIst that will contain the data
mSportsData = new ArrayList<>();
//Initialize the adapter and set it ot the RecyclerView
mAdapter = new SportsAdapter(this, mSportsData);
mRecyclerView.setAdapter(mAdapter);
initializeData(savedInstanceState);
ItemTouchHelper helper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.UP | ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
Collections.swap(mSportsData, from, to);
mAdapter.notifyItemMoved(from, to);
return true;
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
mSportsData.remove(viewHolder.getAdapterPosition());
mAdapter.notifyItemRemoved(viewHolder.getAdapterPosition());
}
});
helper.attachToRecyclerView(mRecyclerView);
}
#Override
protected void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelableArrayList("state", mSportsData);
}
/**
* Method for initializing the sports data from resources.
*/
private void initializeData(Bundle savedInstanceState) {
if(savedInstanceState!=null){
mSportsData.clear();
mSportsData = savedInstanceState.getParcelableArrayList("state");
} else {
//Get the resources from the XML file
String[] sportsList = getResources().getStringArray(R.array.sports_titles);
String[] sportsInfo = getResources().getStringArray(R.array.sports_info);
String[] sportsDetail = getResources().getStringArray(R.array.sports_detail);
TypedArray sportsImageResource = getResources().obtainTypedArray(R.array.sports_images);
//Clear the existing data (to avoid duplication)
mSportsData.clear();
//Create the ArrayList of Sports objects with the titles and information about each sport
for (int i = 0; i < sportsList.length; i++) {
mSportsData.add(new Sport(sportsList[i], sportsInfo[i], sportsDetail[i], sportsImageResource.getResourceId(i, 0)));
}
sportsImageResource.recycle();
}
//Notify the adapter of the change
mAdapter.notifyDataSetChanged();
}
public void resetSports(){
initializeData(null);
}
}
App Images:-
#1 Initial List
#2 Changed List (Card #2 for sport basketball is swiped)
Orientation change to landscape:-
Even though the question is 4 months old and you probably don't need the answer anymore:
The problem is that you initialize the adapter with mSportsData, but reassign another value to the variable later in initializeData(). The ArrayList bound to the adapter is still the empty one it got initialized with.
A way to solve it would be to initialize mSportsData with either a new ArrayList if savedInstanceState is null or else the saved value, and to call initializeData only if savedInstanceState is null. You can remove the argument and therefore the if from initalizeData() completely.
// Initialize the ArrayList that will contain the data.
if (savedInstanceState == null) {
mSportsData = new ArrayList<>();
} else {
mSportsData = savedInstanceState.getParcelableArrayList("state");
}
//Initialize the adapter and set it ot the RecyclerView (nothing changed here)
mAdapter = new SportsAdapter(this, mSportsData);
mRecyclerView.setAdapter(mAdapter);
if (savedInstanceState == null) {
initializeData();
}
Remove this function call from OnCreate initializeData(savedInstanceState); and call it in OnResume
I couldn't get it working with the Parcelable way so what I tried doing (and it worked) was save all the info contained inside all the sport objects in separate lists and put those in the bundle instead.
In the initialiseData(), I just used these lists then to create a new list of sport objects with the exact data.
This shouldn't be the way of doing it but my app just doesn't loads the saved list if it's passed as a parcelable and I don't know why.
Here's the working code for now:-
#Override
protected void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
ArrayList<String> titles = new ArrayList<>();
ArrayList<String> infos = new ArrayList<>();
ArrayList<String> details = new ArrayList<>();
ArrayList<Integer> imgs = new ArrayList<>();
for(Sport sport: mSportsData){
titles.add(sport.getTitle());
infos.add(sport.getInfo());
details.add(sport.getDetail());
imgs.add(sport.getImageResource());
}
outState.putStringArrayList("title", titles);
outState.putStringArrayList("info", infos);
outState.putStringArrayList("detail", details);
outState.putIntegerArrayList("img", imgs);
}
private void initializeData(Bundle savedInstanceState) {
boolean hasData = false;
if(savedInstanceState!=null){
if(savedInstanceState.containsKey("title")){
//Get the resources from the bundle
ArrayList<String> sportsList = savedInstanceState.getStringArrayList("title");
ArrayList<String> sportsInfo = savedInstanceState.getStringArrayList("info");
ArrayList<String> sportsDetail = savedInstanceState.getStringArrayList("detail");
ArrayList<Integer> sportsImageResource = savedInstanceState.getIntegerArrayList("img");
//Clear the existing data (to avoid duplication)
mSportsData.clear();
//Create the ArrayList of Sports objects with the titles and information about each sport
for (int i = 0; i < sportsList.size(); i++) {
mSportsData.add(new Sport(sportsList.get(i), sportsInfo.get(i), sportsDetail.get(i), sportsImageResource.get(i)));
}
hasData = true;
}
}
if(!hasData) {
//Get the resources from the XML file
String[] sportsList = getResources().getStringArray(R.array.sports_titles);
String[] sportsInfo = getResources().getStringArray(R.array.sports_info);
String[] sportsDetail = getResources().getStringArray(R.array.sports_detail);
TypedArray sportsImageResource = getResources().obtainTypedArray(R.array.sports_images);
//Clear the existing data (to avoid duplication)
mSportsData.clear();
//Create the ArrayList of Sports objects with the titles and information about each sport
for (int i = 0; i < sportsList.length; i++) {
mSportsData.add(new Sport(sportsList[i], sportsInfo[i], sportsDetail[i], sportsImageResource.getResourceId(i, 0)));
}
sportsImageResource.recycle();
}
//Notify the adapter of the change
mAdapter.notifyDataSetChanged();
}

Adding Toast to Every ChildItem of a Multi Level Expandable LisView

I found this multilevel expandable listview which is very accurate with my project, but I also want to add a different toast to every third level (which is the last layer of view) child items. I tried this:
expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
#Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
Toast.makeText(thirdLevelq1.put(q1[0] this, "The first child" , Toast.LENGTH_LONG).show();
return false;
}
});
But it gives error. I even tried to use same toast text to every childItem, app installed but crashed when I opened the adapter. So, how to add them properly?
Java:
package com.bacon.expandablelistview;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ExpandableListView;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private ExpandableListView expandableListView;
String[] parent = new String[]{"What is View?", "What is Layout?", "What is Dynamic Views?"};
String[] q1 = new String[]{"List View", "Grid View"};
String[] q2 = new String[]{"Linear Layout", "Relative Layout"};
String[] q3 = new String[]{"Recycle View"};
String[] des1 = new String[]{"A layout that organizes its children into a single horizontal or vertical row. It creates a scrollbar if the length of the window exceeds the length of the screen."};
String[] des2 = new String[]{"Enables you to specify the location of child objects relative to each other (child A to the left of child B) or to the parent (aligned to the top of the parent)."};
String[] des3 = new String[]{"This list contains linear layout information"};
String[] des4 = new String[]{"This list contains relative layout information,Displays a scrolling grid of columns and rows"};
String[] des5 = new String[]{"Under the RecyclerView model, several different components work together to display your data. Some of these components can be used in their unmodified form; for example, your app is likely to use the RecyclerView class directly. In other cases, we provide an abstract class, and your app is expected to extend it; for example, every app that uses RecyclerView needs to define its own view holder, which it does by extending the abstract RecyclerView.ViewHolder class."};
LinkedHashMap<String, String[]> thirdLevelq1 = new LinkedHashMap<>();
LinkedHashMap<String, String[]> thirdLevelq2 = new LinkedHashMap<>();
LinkedHashMap<String, String[]> thirdLevelq3 = new LinkedHashMap<>();
/**
* Second level array list
*/
List<String[]> secondLevel = new ArrayList<>();
/**
* Inner level data
*/
List<LinkedHashMap<String, String[]>> data = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//The problem starts here
expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
#Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
Toast.makeText(getApplicationContext(), "The first child", Toast.LENGTH_LONG).show();
return false;
}
});
setUpAdapter();
}
private void setUpAdapter() {
secondLevel.add(q1);
secondLevel.add(q2);
secondLevel.add(q3);
thirdLevelq1.put(q1[0], des1);
thirdLevelq1.put(q1[1], des2);
thirdLevelq2.put(q2[0], des3);
thirdLevelq2.put(q2[1], des4);
thirdLevelq3.put(q3[0], des5);
data.add(thirdLevelq1);
data.add(thirdLevelq2);
data.add(thirdLevelq3);
expandableListView = (ExpandableListView) findViewById(R.id.expandible_listview);
//passing three level of information to constructor
ThreeLevelListAdapter threeLevelListAdapterAdapter = new ThreeLevelListAdapter(this, parent, secondLevel, data);
expandableListView.setAdapter(threeLevelListAdapterAdapter);
expandableListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {
int previousGroup = -1;
#Override
public void onGroupExpand(int groupPosition) {
if (groupPosition != previousGroup)
expandableListView.collapseGroup(previousGroup);
previousGroup = groupPosition;
}
});
}
}
Thanks in advance.
Edit: When I use Toast.makeText(getApplicationContext(), "The first child", Toast.LENGTH_LONG).show(); its crash logs says:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ExpandableListView.setOnChildClickListener(android.widget.ExpandableListView$OnChildClickListener)' on a null object reference
#Override
public void onFinalChildClick(int plpos, int slpos, int tlpos) {
String msg = "";
switch (tlpos) {
case 0:
msg = "Shakespear is a good poet";
break;
case 1:
msg = "Earth isn't flat";
break;
default:
msg = "Unknown";
}
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
#Override
public void onFinalItemClick(String plItem, String slItem, String tlItem) {
String inMsg = plItem + ", " + slItem + ", " + tlItem;
String outMsg = "";
if (inMsg.equals("group 1, Child Level 1, A")){
outMsg = "Shakespear is a good poet";
} else if (inMsg.equals("group 1, Child Level 1, B")){
outMsg = "Earth isn't flat";
} else {
outMsg = "Unknown";
}
Toast.makeText(this, outMsg, Toast.LENGTH_SHORT).show();
}
Please set this listener expandableListView.setOnChildClickListener after the expandableListView is initialized, that is after setUpAdapter() in your case as expandableListView is still null when you are trying to set the listener because your initialization of this is afterwards.
That is the reason for NullPointerException. because expandableListView is null while setting the listener.
Cheers

Calling a Button's OnClickListener multiple times

I'm using two buttons with the same id in two different layouts in my app where when the first one is clicked, the app loads the 2nd layout and when the button with the same id in the 2nd layout gets clicked, it loads the first layout file. However, my issue is that this toggling happens only once and after that the button doesn't do anything. Do you have any idea on how i can call these onClickListeners whenever each button is clicked until the user leaves that activity?
CardViewActivity.java:
public class CardViewActivity extends AppCompatActivity {
private ImageView cardArtImageView;
private TextView leaderSkillDescText;
private TextView superAttackTitleText;
private TextView superAttackDescText;
private TextView passiveSkillTitleText;
private TextView passiveSkillDescText;
private TextView hpText;
private TextView attText;
private TextView defText;
private TextView costText;
private Button arrowButton;
private int selectedItemPosition;
private boolean isBtnClicked = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.cardview_refined);
// Retrieving the data sent over from MainActivity
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
if (bundle != null) {
selectedItemPosition = bundle.getInt("Card Index");
}
//Toast.makeText(this, "WIDTH: " + SCREEN_WIDTH, Toast.LENGTH_SHORT).show();
// Initializing our views
cardArtImageView = findViewById(R.id.cardArtImageView);
viewDefinitions(false);
setSelectedViewsInit();
initCardViewData(selectedItemPosition);
arrowButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
isBtnClicked = !isBtnClicked;
if (isBtnClicked) {
setContentView(R.layout.cardview_expand_details);
viewDefinitions(true);
initCardViewData(selectedItemPosition);
setSelectedViewsInit();
Log.d("BTN", "Btn Clicked 1st time");
arrowButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
setContentView(R.layout.cardview_refined);
cardArtImageView = findViewById(R.id.cardArtImageView);
viewDefinitions(false);
initCardViewData(selectedItemPosition);
setSelectedViewsInit();
isBtnClicked = !isBtnClicked;
Log.d("BTN", "Btn Clicked 2nd time");
}
});
}
}
});
}
/**
* Sets the required textViews as selected to allow automatic scrolling
*/
private void setSelectedViewsInit() {
leaderSkillDescText.setSelected(true);
superAttackTitleText.setSelected(true);
superAttackDescText.setSelected(true);
if (passiveSkillTitleText != null && passiveSkillDescText != null) {
passiveSkillTitleText.setSelected(true);
passiveSkillDescText.setSelected(true);
}
}
/**
* Adds the views's definitions
*
* #param initPassiveInfo used to decide whether or not the passiveSkillDesc & ..Title != null
* so that they can be defined
*/
private void viewDefinitions(boolean initPassiveInfo) {
leaderSkillDescText = findViewById(R.id.leaderSkillDesc);
superAttackTitleText = findViewById(R.id.superAttackTitle);
superAttackDescText = findViewById(R.id.superAttackDesc);
if (initPassiveInfo) {
passiveSkillTitleText = findViewById(R.id.passiveSkillTitle);
passiveSkillDescText = findViewById(R.id.passiveSkillDesc);
} else {
Log.d("Definitions", "Passive info == null");
}
hpText = findViewById(R.id.HP);
attText = findViewById(R.id.ATT);
defText = findViewById(R.id.DEF);
costText = findViewById(R.id.COST);
arrowButton = findViewById(R.id.arrowButton);
}
/**
* Initialize the cardViewActivity's views with the data from the CardInfoDatabase.java class
*
* #param selectedItemPosition Used to initialize this activity's views if the intent was called from the MainScreen Fragment
*/
private void initCardViewData(int selectedItemPosition) {
if (cardArtImageView != null) {
cardArtImageView.setImageResource(CardInfoDatabase.cardArts[selectedItemPosition]);
}
leaderSkillDescText.setText(CardInfoDatabase.leaderSkills[selectedItemPosition]);
superAttackTitleText.setText(CardInfoDatabase.superAttacksName[selectedItemPosition]);
superAttackDescText.setText(CardInfoDatabase.superAttacksDesc[selectedItemPosition]);
if (passiveSkillTitleText != null && passiveSkillDescText != null) {
passiveSkillTitleText.setText(CardInfoDatabase.passiveSkillsName[selectedItemPosition]);
passiveSkillDescText.setText(CardInfoDatabase.passiveSkillsDesc[selectedItemPosition]);
}
hpText.setText(CardInfoDatabase.hp[selectedItemPosition].toString());
attText.setText(CardInfoDatabase.att[selectedItemPosition].toString());
defText.setText(CardInfoDatabase.def[selectedItemPosition].toString());
costText.setText(CardInfoDatabase.cost[selectedItemPosition].toString());
}
}
To avoid this issue, you need to make sure that the OnClickListener you assign to the button always sets the OnClickListener for the button in the "new" layout.
I haven't tested this, but it seems like it should work in theory. Try defining the listener as a private member of your class, then setting it in your onCreate, like arrowButton.setOnClickListener(arrowClickListener);:
private void arrowClickListener = new View.OnClickListener(){
#Override
public void onClick(View view) {
// clicked buttton -- pick layout based on button "state"
int resId = isBtnClicked ? R.layout.cardview_expand_details : R.layout.cardview_refined;
// set the contentview with the layout we determined earlier
setContentView(resId);
// If we're in the "normal" view, find the card art view and set our field to it
if (!isBtnClicked){
cardArtImageView = findViewById(R.id.cardArtImageView);
}
// do other initialization stuff
viewDefinitions(isBtnClicked);
initCardViewData(selectedItemPosition);
setSelectedViewsInit();
// set our new arrow button click listener to this listener
arrowButton.setOnClickListener(arrowClickListener);
// toggle button flag
isBtnClicked = !isBtnClicked;
}
}
Sorry if I got some of the logic wrong -- the key in this case is to set the click listener "recursively", in a manner of speaking, which ensures that a listener gets set after every click.

Refreshing a ListView with a custom adapter

I am writing a small google talk client for android and I am having trouble refreshing my ListView correcty.
This list contains the contact list and is showing the name and the presence of the contact. My listener works fine and I can see the presence changes of each contact in the log cat window, but my ListView is not refreshing... here is some code:
package de.marc.messenger;
// ofc here are the imports
public class RosterActivity extends Activity {
private Roster _roster;
private XMPPConnection _connection;
private List<HashMap<String, String>> _buddies;
private BuddyAdapter _adapter;
private ListView _list;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.roster);
_buddies = new ArrayList<HashMap<String, String>>();
_connection = LoginActivity.CONNECTION;
makePauseForRoster();
_roster = _connection.getRoster();
addRosterListener();
fillBuddyList();
sortBuddyList();
initializeListView();
}
/**
* Lets the thread sleep for a second to ensure that the presence of every
* user will be available
*/
private void makePauseForRoster() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* Adds a listener to the roster, primarily for changes of presence
*/
private void addRosterListener() {
_roster.addRosterListener(new RosterListener() {
public void presenceChanged(Presence presence) {
String user = presence.getFrom().split("/")[0];
HashMap<String, String> buddy = findBuddyInRoster(user);
String p = getPresenceString(user);
buddy.put("presence", p);
System.out.println(buddy.values().toString()); // this works
_adapter.notifyDataSetChanged(); // this doesn't
_list.invalidate(); // this neither
}
});
}
/**
* Fills the list view with the roster entries
*/
private void initializeListView() {
_adapter = new BuddyAdapter(this, R.layout.roster_item,
_buddies);
_list = (ListView) findViewById(R.id.list_roster);
_list.setAdapter(_adapter);
}
/**
* Fills the buddy list with relevant data from a RosterEntry. Relevant data
* is the users' name, email and presence
*/
private void fillBuddyList() {
// this just fills my list of hashmaps (_buddies)
}
/**
* Get a predefined String depending on the presence of a user
*/
private String getPresenceString(String user) {
// something like "available: away ()" -> "away"
}
/**
* Sorts the buddy list. Only criterion is the presence of the user, because
* we have linear algorithms for this kind of problem.
*/
private void sortBuddyList() {
// move all offline contacts to the end
// move all online contacts to the beginning
// all other kind of contacts will stay in the middle
}
/**
* Finds a specific buddy object for a user via his hashed email
*/
private HashMap<String, String> findBuddyInRoster(String user) {
for (HashMap<String, String> buddy : _buddies) {
if (user.equals(buddy.get("user"))) {
return buddy;
}
}
return null;
}
}
This works fine, everything is shown correctly.. only trouble seems to be in the addRosterListener() method, where the onPresenceChanged() is implemented..
Here is my adapter:
package de.marc.messenger;
// as well some imports
public class BuddyAdapter extends ArrayAdapter<HashMap<String, String>> {
private Context _context;
private List<HashMap<String, String>> _map;
private LayoutInflater _inflater;
public BuddyAdapter(Context context, int id, List<HashMap<String, String>> map) {
super(context, id, map);
_context = context;
_map = map;
_inflater = (LayoutInflater) _context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// find view of a single row in a listview
View row = convertView;
if (convertView == null) {
row = _inflater.inflate(R.layout.roster_item, null);
}
// get data for a specific row
String name = _map.get(position).get("name");
String user = _map.get(position).get("user");
String presence = _map.get(position).get("presence");
// extract views from the row view
TextView nameText = (TextView) row.findViewById(R.id.text_name);
TextView userText = (TextView) row.findViewById(R.id.text_id);
ImageView presenceImg = (ImageView) row
.findViewById(R.id.image_presence);
// set data in extracted views
nameText.setText(name);
userText.setText(user);
int resource = 0;
// something is done with this variable
presenceImg.setImageResource(resource);
return row;
}
}
Is there anything I am missing?
Edit: I changed my onPresenceChanged method like this:
public void presenceChanged(Presence presence) {
String user = presence.getFrom().split("/")[0];
HashMap<String, String> buddy = findBuddyInRoster(user);
_adapter.remove(buddy);
String p = getPresenceString(user);
buddy.put("presence", p);
_adapter.add(buddy);
_adapter.notifyDataSetChanged();
}
It works to some extend: After swiping a bit on the screen, the contact that changed his presence is now out of the list :/
When an ArrayAdapter is constructed, it holds the reference for the List that was passed in. If you were to pass in a List that was a member of an Activity, and change that Activity member later, the ArrayAdapter is still holding a reference to the original List . The Adapter does not know you changed the List int he Activity.
It looks to me like that's what you're doing, so you need to recreate the adapter with the new list data.
Or you can use the ArrayAdapter methods to modify the underlying List (add, insert, remove, clear, etc.) Then notifyDataSetChanged wil work.

Drawing to a canvas using a custom method?

I'm coding for an android 2-D game and I'm having some trouble.
Currently, in my package, I have a thread which draws on an instance of a canvas. I'm drawing Drawable bitmaps from resources. What I would like to do is keep the thread handing the drawing of the background image. But have instances of custom objects able draw to that same canvas using drawable objects. This seems possible logically, but I can't get it to work. Any time I attempt to retrieve the resources in my custom class the app crashes on start-up.
Here were a couple of my efforts: (please don't laugh if i've done something stupid, i'm trying.)
public class Worker{
//Get drawables
// Resources res = getResources();
// Drawable man1 = res.getDrawable(R.drawable.workertest);
// Context mContext;
// Resources res = mContext.getResources();
// Drawable man1 = mContext.getResources().getDrawable(R.drawable.workertest);
// Drawable man2 = mContext.getResources().getDrawable(R.drawable.workertest1);
// Drawable man1 = res.getDrawable(R.drawable.workertest);
// Drawable man2 = res.getDrawable(R.drawable.workertest1);
}
As you can see I've tried a couple different methods, and I've also tried adding (extends activity) to my class, but I can't figure this out.
This code doesn't address actually drawing to the canvas, I have yet to cross that bridge
Edit: Here is the Activity. As you can see it calls a thread in lunarview. That thread instantiates the object that needs to retrieve the Drawable. how in gods name do i get the application context to the object? or is there another way to do it? And yes, this is the code from the android sample codes.
public class LunarLander extends Activity {
private static final int MENU_EASY = 1;
private static final int MENU_HARD = 2;
private static final int MENU_MEDIUM = 3;
private static final int MENU_PAUSE = 4;
private static final int MENU_RESUME = 5;
private static final int MENU_START = 6;
private static final int MENU_STOP = 7;
/** A handle to the thread that's actually running the animation. */
public static LunarThread mLunarThread;
/** A handle to the View in which the game is running. */
public LunarView mLunarView;
/**
* Invoked during init to give the Activity a chance to set up its Menu.
*
* #param menu the Menu to which entries may be added
* #return true
*/
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, MENU_START, 0, R.string.menu_start);
menu.add(0, MENU_STOP, 0, R.string.menu_stop);
menu.add(0, MENU_PAUSE, 0, R.string.menu_pause);
menu.add(0, MENU_RESUME, 0, R.string.menu_resume);
menu.add(0, MENU_EASY, 0, R.string.menu_easy);
menu.add(0, MENU_MEDIUM, 0, R.string.menu_medium);
menu.add(0, MENU_HARD, 0, R.string.menu_hard);
return true;
}
/**
* Invoked when the user selects an item from the Menu.
*
* #param item the Menu entry which was selected
* #return true if the Menu item was legit (and we consumed it), false
* otherwise
*/
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_START:
mLunarThread.doStart();
return true;
case MENU_STOP:
mLunarThread.setState(LunarThread.STATE_LOSE,
getText(R.string.message_stopped));
return true;
case MENU_PAUSE:
mLunarThread.pause();
return true;
case MENU_RESUME:
mLunarThread.unpause();
return true;
case MENU_EASY:
mLunarThread.setDifficulty(LunarThread.DIFFICULTY_EASY);
return true;
case MENU_MEDIUM:
mLunarThread.setDifficulty(LunarThread.DIFFICULTY_MEDIUM);
return true;
case MENU_HARD:
mLunarThread.setDifficulty(LunarThread.DIFFICULTY_HARD);
return true;
}
return false;
}
/**
* Invoked when the Activity is created.
*
* #param savedInstanceState a Bundle containing state saved from a previous
* execution, or null if this is a new execution
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// tell system to use the layout defined in our XML file
setContentView(R.layout.lunar_layout);
// get handles to the LunarView from XML, and its LunarThread
mLunarView = (LunarView) findViewById(R.id.lunar);
mLunarThread = mLunarView.getThread();
// give the LunarView a handle to the TextView used for messages
mLunarView.setTextView((TextView) findViewById(R.id.text));
if (savedInstanceState == null) {
// we were just launched: set up a new game
mLunarThread.setState(LunarThread.STATE_READY);
Log.w(this.getClass().getName(), "SIS is null");
} else {
// we are being restored: resume a previous game
mLunarThread.restoreState(savedInstanceState);
Log.w(this.getClass().getName(), "SIS is nonnull");
}
}
/**
* Invoked when the Activity loses user focus.
*/
#Override
protected void onPause() {
super.onPause();
mLunarView.getThread().pause(); // pause game when Activity pauses
}
/**
* Notification that something is about to happen, to give the Activity a
* chance to save state.
*
* #param outState a Bundle into which this Activity should save its state
*/
#Override
protected void onSaveInstanceState(Bundle outState) {
// just have the View's thread save its state into our Bundle
super.onSaveInstanceState(outState);
mLunarThread.saveState(outState);
Log.w(this.getClass().getName(), "SIS called");
}
public boolean onTouchEvent(MotionEvent event){
int clickX = Math.round(event.getX()/10)*10;
int clickY = Math.round(event.getY()/10)*10;
LunarView.xCo = clickX;
LunarView.yCo = clickY;
return true;
}
As Greg suggested, the problem is in getting the right Context to retrieve the resources.
An easy way to solve this is to pass the main Activity as an argument in the Worker class constructor, and save it as a private Context field. Something like:
public class Worker{
private Context mContext;
public Worker (Context context) {
mContext = context;
Resources res = mContext.getResources();
// all the rest of your code
}
}
You will use it in your main activity as:
Worker worker = new Worker(this);

Categories