Android - call method instead of class - sdk - java

I have my main application class as follows and what I would like to know is how to change one line to call a method either from same class or another class, whilst the others still call activities. Here is the code:
public class InfoActivity extends GDListActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(R.string.info_activity_title);
ItemAdapter adapter = new ItemAdapter(this);
adapter.add(createTextItem(R.string.info_about, AboutActivity.class));
adapter.add(createTextItem(R.string.info_terms, TermsActivity.class));
adapter.add(createTextItem(R.string.info_privacy, PrivacyActivity.class));
setListAdapter(adapter);
}
private TextItem createTextItem(int stringId, Class<?> klass) {
final TextItem textItem = new TextItem(getString(stringId));
textItem.setTag(klass);
return textItem;
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
final TextItem textItem = (TextItem) l.getAdapter().getItem(position);
Intent intent = new Intent(InfoActivity.this, (Class<?>) textItem.getTag());
startActivity(intent);
}
}
The line in question:
adapter.add(createTextItem(R.string.info_about, AboutActivity.class));
I would like to call a method which as an example does this:
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] { AboutActivity.this.getString(R.string.feedback_email) } );
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, AboutActivity.this.getString(R.string.feedback_subject));
emailIntent.setType("plain/text");
startActivity(emailIntent);
Right now I have this in the onCreate method of AboutActivity, but there naturally is no reason to have this functionality (sending email) within an activity. Instead it can just be ran as is. So, how could I do this?
Thanks!
The other two lines:
adapter.add(createTextItem(R.string.info_terms, TermsActivity.class));
adapter.add(createTextItem(R.string.info_privacy, PrivacyActivity.class));
they can remain the same in terms of functionality. This question is an addendum from this one (which I asked earlier and got answered):
Android - call method instead of class - sdk

How ever you choose to go about this, the trick is to actually move the decision making about what to do when a particular item is clicked down into the onListItemClick method. Here's a simple approach. First, change your createTextItem method to this:
private TextItem createTextItem(int stringId) {
final TextItem textItem = new TextItem(getString(stringId));
return textItem;
}
Then, change your onListItemClick to this:
protected void onListItemClick(ListView l, View v, int position, long id) {
final TextItem textItem = (TextItem) l.getAdapter().getItem(position);
String textItemContents = textItem.getString(); //I don't know if this is actually correct. I don't know what the TextItem class is. But I think you get the idea.
Intent intent = getIntentForString(textItemContents);
startActivity(Intent);
}
Your getIntentForString() method would then look something like this (note we can't use a switch because support for using switch statements with strings has only just recently been added to java):
private Intent getIntentForString(String textViewContents){
if(textViewContents.equals(getString(R.string.info_about))){
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] {
AboutActivity.this.getString(R.string.feedback_email) } );
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
AboutActivity.this.getString(R.string.feedback_subject));
emailIntent.setType("plain/text");
return emailIntent;
}
else if(textViewContents.equals(getString(R.string.info_terms)){
return new Intent(InfoActivity.this, TermsActivity.class);
}
else if(textViewContents.equals(getString(R.string.info_privacy)){
return new Intent(InfoActivity.this, Privacy.class);
}
else{
return null;
}
}
Note this approach has a downfall though. If yous start adding a bunch of different items to your ListView you're going to need to grow and grow your getIntentForString() method. For right now though, this should suffice. If you find yourself adding more options to your ListView though they'll be a more complicated approach that we'll need to take.

Related

Passing a Boolean ArrayList and a String ArrayList to the next Activity

I want to pass an ArrayList<String> and an ArrayList<Boolean> to my next activity, but whatever I try, it always seems to overwrite my first ArrayList with my second. Even if I use a boolean array in stead of an ArrayList, the same problem occurs. The KEY's are different and the values as well. I tried many things already (can't remember all of it), including Bundles, but the same keeps happening. What am I doing wrong?
In both cases (and many others), data comes back null.
EDIT: Cleaned up code after a night's sleep, as I noticed there were indeed some mistakes in it. Also, these are 2 ways that I tried to do it, but neither work.
In activity 1:
boolean [] checksDataOut;
private ArrayList<String> data = new ArrayList();
intent.putStringArrayListExtra(EXTRA_TODEVICES, data);
intent.putExtra(EXTRA_TO_DEVICES, checksDataOut);
startActivity(intent);
In Activity 2
public class DevicesInfo extends AppCompatActivity {
public ArrayList<String> data = new ArrayList();
public boolean [] checksDataIn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_devices_info);
Intent intent = getIntent();
data = intent.getStringArrayListExtra(EXTRA_TODEVICES);
checksDataIn = intent.getBooleanArrayExtra(EXTRA_TO_DEVICES);
}
}
In activity 1:
private ArrayList<String> data = new ArrayList();
boolean [] checksDataOut;
Bundle extrasOut = new Bundle();
extrasOut.putStringArrayList(EXTRA_TODEVICES, data);
extrasOut.putBooleanArray(EXTRA_TO_DEVICES, checksDataOut);
intent.putExtras(extrasOut);
startActivity(intent);
In activity 2:
public class DevicesInfo extends AppCompatActivity {
public ArrayList<String> data = new ArrayList();
public ArrayList<Boolean> checksData = new ArrayList();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_devices_info);
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
data = bundle.getStringArrayList(EXTRA_TODEVICES);
checksData = (ArrayList<Boolean>) bundle.get(EXTRA_TO_DEVICES);
}
}
Personally, I prefer placing that variable in the main activity, the activity which will only die when I close the program. I put it there as a public static variable. So when I launch a new activity, I just instruct it to retrieve data from the variable onCreate or onResume. I know some may say, it's poor programming. At least I can control the content effectively.
In base Activity: or Activity1 create these static fields:
public static ArrayList<String> data;
public static ArrayList<Boolean> checksData;
just before you call Activity2, update these 2 variables.
then call the Activity2 normally.
as Activity2 is created or resumed, where you need data, call data in Activity1, like this Activity1.data; where you want to use checksData, call Activity1.checksData.
First, If you plan on using getBooleanArray, you need to pass a boolean[] using putExtra not a list.
Second, it's null if it cannot be found. It's not clear where your constants are defined but they must be the same, so try using a regular string while debugging.
Putting
boolean[] checksDataOut;
...
Bundle extrasOut = new Bundle();
extrasOut.putBooleanArray("boolArray", checksDataOut);
intent.putExtras(extrasOut);
// or intent.putExtra("boolArray", checksDataOut);
startActivity(intent);
Getting
public boolean[] checksDataIn;
...
Intent intent = getIntent();
checksDataIn = intent.getBooleanArrayExtra("boolArray");
If it's still null, put a break point or log the entire contents of the Bundle to the logcat.

Start a particular intent using ArrayList

I want to start an explicit intent , Here is the code
public class TabView extends ListActivity {
List<String> list1strings = new ArrayList<String>();
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.tview);
ListView listview1 = (ListView) findViewById(R.id.list1);
TabHost th = (TabHost) findViewById(android.R.id.tabhost);
th.setup();
list1strings.add("Programming Language");
list1strings.add("Database");
list1strings.add("Java Technologies");
list1strings.add("Web Technologies");
list1strings.add("Application/Web Server");
list1strings.add("operating Systems");
list1strings.add("PHP framework");
list1strings.add("PHP CMS");
listview1.setAdapter(new ArrayAdapter<String>(TabView.this,
android.R.layout.simple_list_item_1, list1strings));
th.addTab(th.newTabSpec("Tab1").setIndicator("Training")
.setContent(R.id.tab1));
TabSpec specs = th.newTabSpec("Tab2");
specs.setContent(R.id.tab2);
specs.setIndicator("Workshops");
th.addTab(specs);
specs = th.newTabSpec("Tab 3");
specs.setContent(R.id.tab3);
specs.setIndicator("Reach Us");
th.addTab(specs);
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
super.onListItemClick(l, v, position, id);
String getpos = list1strings.get(position);
try {
Class<?> ourclass = Class.forName("in.brainwave.industrialtraining." + getpos);
Intent ourIntent = new Intent(TabView.this, ourclass);
startActivity(ourIntent);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
"list1string" here have data and I want to start an intent when an item on ListView is clicked , but the problem is that class name cannot have space and "String getpos = list1strings.get(position);" returns the string from ArrayList which contains space , Is there any way so that I can start an Intent when any item is clicked.
Please guide me .
You can just initialize a second array/list with the classes of each item, so when you select item N you get the class at the same position. For example:
ArrayList<String> listItems = new ArrayList<String>();
ArrayList<Class> classes = new ArrayList<Class>();
listItems.add("Programming");
classes.add(YourProgrammingActivity.class);
// (...)
listItems.add("PHP CMS");
classes.add(YourPHPCMSActivity.class);
And then to start the right activity in the listener:
Class activityToStart = classes.get(position);
Intent ourIntent = new Intent(TabView.this, activityToStart);
startActivity(ourIntent);
As you can guess, each class you put in that list should be an activity (and be declared in AndroidManifest), otherwise your app will crash.
Also if this is a fixed list, replacing ArrayList with arrays is much less verbose:
String[SIZE] listItems = {"Programming", /* (...) */, "PHP CMS"};
Class[SIZE] classes = {YourProgrammingActivity.class, /* (...) */, YourPHPCMSActivity.class };
(...)
Class activityToStart = classes[position];
From an Object Oriented Perspective you can encapsulate this information in one class and do a for loop to do it in an automatic manner. Let me show you (and remember there are multiple possible options, in doing this).
Keep in mind that this is more or less pseudocode that looks exactly to Java but I programed from memory, so there could be errors and typos!
First of all let met tell you that there are some things that are done in a "bad" way. For example this piece of code will never work:
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
String getpos = list1strings.get(position);
try {
Class<?> ourclass = Class.forName("in.brainwave.industrialtraining." + getpos);
Intent ourIntent = new Intent(TabView.this, ourclass);
startActivity(ourIntent);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
Because you are storing Strings which are titles! Not even Java class names. The best way is to create a class that encapsulate the information you need. This class keeps all information you need to provide to the different components.
public class TabEntry {
// In here you save the real class to be invoked in the listener
private Class mEntryClass;
// You store the title of the tab
private String mTabTitle;
// In here you store the content tab id like R.id.tab3
private int mContentId;
// Add more fields as necessary
...
public TabEntry(Class clazz, String tabTitle, int contentId) {
this.mClass = clazz;
this.mTabTitle = tabTitle;
}
public Class getEntryClass() {
return this.mEntryClass;
}
public String getTabTitle() {
return this.mTabTitle;
}
// Add more getters as needed
}
So right now, you have created a class that encapsulates the information you need to create your tabs. Now in the code of the TabActivity:
public class TabView extends ListActivity {
List<TabEntry> mListTabEntries = new ArrayList<TabEntry>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tview);
ListView listview1 = (ListView) findViewById(R.id.list1);
TabHost th = (TabHost) findViewById(android.R.id.tabhost);
th.setup();
// Initalize your list with your tabs entries, this should be done outside this method
// to don't clutter your code
mListTabEntries.add(new TabEntry(Activity1.class, "Title", R.id.text1));
mListTabEntries.add(new TabEntry(Activity2.class, "Title2", R.id.text2));
listview1.setAdapter(new ArrayAdapter<String>(TabView.this,
android.R.layout.simple_list_item_1, getTabsStrings(mListTabEntries)));
// Create a for loop to iterate each entry in your list, so you dont have to do it
// manually
for (TabEntry entry : mListTabEntries) {
th.addTab(th.newTabSpec(entry.getTabTitle())
.setIndicator(entry.getIndictor())
.setContent(entry.getContent());
}
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
TabEntry entry = list1strings.get(position);
Intent ourIntent = new Intent(TabView.this, entry.getTabClass());
startActivity(ourIntent);
}
private String[] getTabsStrings(List<TabEntry> mEntries) {
String[] titlesString = new String[mEntries.size()];
for(int i = 0; i < mEntries.length(); i++) {
titlesString[i] = mEntries.get(i);
}
return titlesString;
}
}
At this point this code should work (but IMHO it not the prettiest one). So we can do better (if you want learn more about code, you can read Clean Code or Effective Java, both books are great to improve your skills with programming).
If you want to learn more about how we can make this code more pretty and succinct let me know! :)

Android - How to make an Activity for each item in a ListView and pass Java Object to next screen

I have a ListView of contacts and each contact is a Java object with a bunch of information associated with it. Some of that info is shown in the ListView, but the rest is meant for the DetailView. I'm trying to write a method that will take me to a DetailView for any contact I click on, but I also need to take the object with me. I store all of the contacts in an ArrayList in my MainActivity.java
My questions: Do I need to take the contact object with me or is there actually some way to access my ArrayList in another Activity?
If I can/have to take it with me, how would I do so, since the putExtra() methods don't take objects as arguments.
The beginning of my MainActivity looks like this:
public class MainActivity extends Activity implements AdapterView.OnItemClickListener {
ListView list;
I have a very basic onClickListener right now but don't know what to put into it:
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent intent = new Intent(MainActivity.this, DetailView.class);
intent.putExtra("contact", i);
startActivity(intent);
}
I feel like my initial definition of the MainActivity class doesn't correspond to the onItemClick method or something, which is why it doesn't execute and take me to another screen.
Intent intent = new Intent(ActivityA.this, DetailView.class);
intent.putExtra("extra_name","extra");
startActivity(intent);
Then in the other Activity :
/*onCreate*/
Object extra = getIntent().getExtra().get("extra_name");
I would recommend to pass an ID of some sort pointing to the description, passing complex data structure with intents is not recommended
Simply, use Intent.putExtra() to pass the information and getIntent().getExtras() to receive the information.
// call a new intent
myIntent = new Intent(ActivityA.this, ActivityB.class);
// put an extra (like this following string)
String userid = "User A";
myIntent.putExtra("TAG_CONTACT_ID", userid);
startActivity(myIntent);
// receive the info in your onCreate method in ActivityB
Bundle extras = getIntent().getExtras();
if(extras != null) {
String newUserID = extras.getString("TAG_CONTACT_ID");
}
See this short example for more information.
If you want to pass an Integer, you can do it as the following: myIntent.putExtra("value", i); and take it with: int myValue = extras.getInt("value");.
Hope this help.
Try something like this:
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Contact c = (Contact)contacts.get(position);
Intent i = new Intent();
i.putExtras("contact", c);
i.setClass(this, Activity2.class);
startActivity(i);
}
Create java object with parcelable
Check the below code to create parcelable object in Android.
public class ObjFeedImages implements Parcelable {
public int image_id;
public boolean like_status = false;
public String image_url = null;
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStringArray(new String[] {
String.valueOf(image_id),
String.valueOf(like_status),
image_url, });
}
public ObjFeedImages(Parcel in) {
String[] data = new String[3];
int i = 0;
in.readStringArray(data);
image_id = Integer.parseInt(data[i++]);//
like_status = Boolean.parseBoolean(data[i++]);
image_url = data[i++];
}
public static final Parcelable.Creator<ObjFeedImages> CREATOR = new Parcelable.Creator<ObjFeedImages>() {
#Override
public ObjFeedImages createFromParcel(Parcel source) {
try {
return new ObjFeedImages(source);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
#Override
public ObjFeedImages[] newArray(int size) {
try {
return new ObjFeedImages[size];
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
};
}
After creating some list of such object say
ObjFeedImages object = new ObjFeedImages();
object.image_id = 1;
object.like_status = true;
object.image_url="http://some image url";
Intent intent = new Intent(/*Your Intent Info*/);
intent.putExtras("key_name",object);
startActivity(intent);
Here is the complete description.
To retrive object in other activity you have to write below code.
ObjFeedImages objectOnOtherActivity = (ObjFeedImages)getIntent().getParcelableExtra("key_name");
So ready to enjoy code.
Happy coding....

How to make listView say one thing, but pass through different data

So this question is a little hard to explain, but basically what i want is to have an array list for a listview, but change the title on the listview. So that when the user sees the listview it says one thing, but passes through different information.
Here's my code:
//the string names
String names[] = { "item1", "item2"};
i have 2 classes called item1.java and item2.java
when one listitem is clicked it sets a class equal to the activity name and opens it like this:
protected void onListItemClick(ListView lv, View v, int position, long id){
super.onListItemClick(lv, v, position, id);
String openClass = names[position];
try{
Class selected = Class.forName("com.example.example." + openClass);
Intent selectedIntent = new Intent(this, selected);
startActivity(selectedIntent);
}catch (ClassNotFoundException e){
e.printStackTrace();
}
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
finish();
}
But in the listview i don't want it to say "item1" and "item2"....is there any way to make like an alias or something?
Sorry if my question is hard to understand i tried my best to explain what i need help with let me know if you have any questions about my question(: thanks for the help
You do this the same way you make any custom adapter, such as one that should display images instead of or in addition to some text.
For example, if your adapter is an ArrayAdapter, you create a subclass, override getView(), and do what you want in there to format your rows the way that you want.
You can override toString() to do this. See following example
public static class Alias {
String originalValue;
public Alias(String s) {
this.originalValue = s;
}
#Override
public String toString() {
return "What are you want to show";
}
}
String names[] = {"item1", "item2"};
Alias array[] = new Alias[names.length];
Alias array[0] = new Alias(names[0]);
Alias array[1] = new Alias(names[1]);
ArrayAdapter<Alias> adapter = new ArrayAdapter<Alias>(this, resId, array);
This will show "What are you want to show" in every listview whatever the value is.

How to test a ListActivity?

I'm new to Android development as well as test-driven development. I want to write unit tests for the following ListActivity:
public class TrendsMainActivity extends ListActivity {
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
String[] list_items = getResources().getStringArray(R.array.trend_menu_names);
setListAdapter(new ArrayAdapter<String>(this, R.layout.main, list_items));
}
#Override
protected void onListItemClick(ListView listView, View view, int position, long id)
{
Intent intent = null;
switch(position)
{
case 0:
intent = new Intent(this, TrendingActivity.class);
break;
case 1:
intent = new Intent(this, SearchActivity.class);
break;
case 2:
intent = new Intent(this, TimelineActivity.class);
break;
}
if(intent != null)
{
startActivity(intent);
}
else
{
Log.e(getClass().getSimpleName(), "There was an error retrieving request.");
}
}}
I have scoured all of the documentation that I can find, but I can not figure out how to test this Activity. The onListItemClick method is not finished, but it gives the idea of what I want to accomplish. I want to test clicking the first item in the ListView, and test that the correct Activity is being started.
How can I accomplish this?
Edit: I want my test to "click" on an item in the ListView. I then want to assert that the activity started is the correct activity (e.g. Clicking ListView item 0 starts the TrendingActivity specifically)
I should say that if you were applying TDD you should have started writing the tests not the application.
Anyway, Android Application Testing Guide contains in chapter 3 two examples that combined together can give you the solution you are looking for. The idea is to use an ActivityMonitor to verify that the expected activity was started.
#UiThreadTest
public void testListItemClickStartsActivity() {
final Instrumentation inst = getInstrumentation();
final IntentFilter intentFilter = new IntentFilter();
// here add conditions to your filter, i.e. intentFilter.addAction()
ActivityMonitor monitor = inst.addMonitor(intentFilter, null, false);
assertEquals(0, monitor.getHits());
// here perform desired click on list
monitor.waitForActivityWithTimeout(5000);
assertEquals(1, monitor.getHits());
inst.removeMonitor(monitor);
}

Categories