Which View does one choose while moving to another Android Activity - android

After much research and tutorial hunting, I've come to find some answers to questions I had on my own. Now, my last question is this: If I have a View instance variable carrying over from the previous Activity, and it isn't working to refine the findViewbyId() function to the current source file, what should I take my View as, to instance my findViewById()?
Here's the code I have:
public class login_fb extends Activity implements View.OnClickListener {
private static final String EMAIL = "email";
private static AccessToken accessToken = null;
private static boolean isLoggedIn = false;
private static String deduction = "";
private static String income = "";
private static CallbackManager callbackManager = CallbackManager.Factory.create();
private static boolean hasItemized = false;
private FireMissiles fireMissiles = new FireMissiles();
public void gotoRcpts (View view)
{
signIn( view );
}
public void getSuggestions(View view) throws ParserConfigurationException, ParseException, SAXException, IOException {
checkPermsOfStorage( this );
setContentView( R.layout.content_search );
Intent intent = Intent.makeMainActivity( lists.newComponentActivity().getComponentName() );
startActivity( intent );
lists listing = new lists(view, view.getContext()); //everything works to here.
listing.display( view ); // This is where I call the function below.
}
Here's the second half of the code
public void display(View view)
{
// I feel the issue is here, but why can I get
listViewP = view.findViewById( R.id.view_personal );
listViewB = view.findViewById( R.id.view_business );
// now create an adapter class
Log.d("&&&&", String.valueOf(this.mpTitle.size()));
if (this.mpTitle != null && listViewP != null) { //if I don't check for null, error
MyAdapter adapterP = new MyAdapter( this, listed_views, mpTitle, content_search, mContext);
listViewP.setAdapter( adapterP ); // here's where my error is.
}
if (this.mbTitle != null && listViewB != null) { // if I don't check for null error
MyAdapter adapterB = new MyAdapter( this, listed_views, mbTitle, content_search, mContext);
listViewB.setAdapter( adapterB ); // here also there's an error.
}
}
The two ListViews are view_personal, and view_business.

A View is a UI component - when your Activity starts, you usually call setContentView in onCreate, passing in an XML layout file. What that does is construct all the different Views in your layout file, setting their parameters, and assigning things like the id you've given them.
So you end up with a layout hierarchy, with things like a TextView that has an id of titleText or whatever. You can find that specific object by calling findViewById() on a View (usually the top-level layout), and it will search all that View's descendants in the layout hierarchy, until it finds one matching that ID name.
Those Views are all instances that get created, if you destroy the activity and recreate it you'll have a whole new set of view objects. So you're not keeping instances (and you definitely shouldn't try to either!), and if you go to another Activity, you definitely aren't seeing the same view objects again.
So when you call findViewById in the new activity, using the same id as in the last activity, you're not finding the same instance of a View object - you're just finding one that's been assigned the same ID. You can reuse IDs like this, the only problem is if you have more than one in the same layout hierarchy - findViewById will only find the first one that matches.
If you're getting null references for listViewP etc., you don't have a view with that ID in your layout (or at least, not under the View you're calling findViewById on). If they exist in another Activity, then you can't access them - that activity isn't running, it doesn't exist. If ActivityB wants to change something in ActivityA, it needs to pass data to it, so ActivityA can handle it.

Related

How get value from adapter class to activity Class

I am having adapter class, In that, I need to pass invoiceId to an Activity Class. I have seen some example like pass-through interface, but I lost track on following the code procedure.
Here Is My Adapter Class extends BaseAdapter
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
companyName = ct.getSharedPreferences("prefs", 0);
Log.d("test", "" + deliveryListBeans.size());
LayoutInflater inflater = (LayoutInflater) ct.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.list_vew_for_delivery_order, null);
TextView invoice = (TextView) v.findViewById(R.id.invoice);
final TextView delivery = (TextView) v.findViewById(R.id.do_delivery);
final DeliveryListBean dlb = deliveryListBeans.get(position);
invoice.setText(dlb.getInvoiceNo());
}
delivery.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ct.startActivity(new Intent(ct, EmployeesListForPopUp.class));
DeliveryOrdersListAdapter deliveryOrdersListAdapter=new DeliveryOrdersListAdapter(EmployeesListForPopUp.this);
}
});
}
Here is My Activity Class
public class EmployeesListForPopUp extends Activity {
private List<EmployeeIdNameBean> employeeIdNameBeans = new ArrayList<EmployeeIdNameBean>();
ListView listView;
SharedPreferences companyName;
EmployeePopUpAdapter employeePopUpAdapter;
private ImageView img1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_employees_list_for_pop_up);
I need to get invoiceId from Adapter Class. How?
You need to pass context of the activity in adapters constructor.
Then set activity.invoiceid value in clickevents of adapter.
One simple way is that you write a method in MainActivity
public void setInvoiceId(int invoiceId) {
// do what you want with invoiceId
}
and pass the instance of your activity to adapter
DeliveryOrdersListAdapter adapter = new DeliveryOrdersListAdapter(EmployeesListForPopUp.this);
and get it in your adapter and keep it
EmployeesListForPopUp myActivity;
public MyAdapter(EmployeesListForPopUp activity) {
myActivity = activity;
}
and where you need to pass invoiceId just call the method of main activity
myActivity.setInvoiceId(invoiceId);
General way of implementing it:
In the adapter class, where you set text to invoice TextView, you also can add a tag to it. Put attention - despite every item in the list is build from the same prototype, the tag (as well as text) will be uniq. The best way is to use "position" as value of the tag: invoice.setText(dlb.getInvoiceNo());
invoice.setTag(Integer.valueOf(position).toString());
You need to make your items in the list clickable (this is out of the scope of this question). So, when you click on some item - you can retrieve any data it has, and specifically tag - getTag();.
Then you send Intent to other activity, providing the tag as extra message. So that activity will "know" which item in the array list it is related to (i.e. tag == position, right?). And continue from there.
I implemented simple project that illustrates it. This project is simple demo and illustration of working with ArrayList adapter,
displaying the item in the ListView, clicking on some item and display relevant data in separated activity. Please download it and try (min API 21). Basic description is available in README file.
The project is here on the GitHub:
(corrected path)
https://github.com/everall77/ArrayListSimpleExmpl

Call finish() within a View.OnClickListener implementation class

There is an Activity that displays listItems (through a cursorAdapter).
The listItem's XML contains some buttons. In the Cursor Adapter's newView() method, these buttons get the onClickListener, not by an anonymous declaration, there's a class that implements the listener. If there's a click on a certain button, the activity where all that happens, should finish.
I'm not surprised that calling finish() in the button class doesn't work. activityContext.finish doesn't work either.
So how can I manage that?
public class DetailActvityActionBtn implements View.OnClickListener {
private Context context;
#Override
public void onClick(View view){
context = view.getContext();
System.out.println("CONTEXT:" + context);
///Itemroot
LinearLayout root =(LinearLayout) view.getRootView().findViewById(R.id.detailRoot);
///Tag that stores data
ItemViewAndDataHolder holder = (ItemViewAndDataHolder) root.getTag();
System.out.println("HOLDER: " + holder.toString());
//Get id of item
int id = holder.getId();
//Get quantity of item
int quantity = Integer.parseInt(holder.getQuantity().getText().toString().replaceAll("[^0-9]",""));
///Append id to URI
Uri updateItemUri = ContentUris.withAppendedId(InventoryDB_Contract.entries.CONTENT_URI, id);
///To determine the clicked button, get ID as String
String btnIDasString = context.getResources().getResourceName(view.getId());
System.out.println(btnIDasString);
ContentValues values = new ContentValues();
int updatedRow;
switch (btnIDasString){
case "com.example.android.inventoryapp:id/plusBtn":
System.out.println("plus");
values.put(InventoryDB_Contract.entries.COLUMN_PRODUCT_QUANTITY_IN_STOCK, quantity + 1);
context.getContentResolver().update(updateItemUri, values, null, null);
//CRcaller.saleItem(1);
break;
case "com.example.android.inventoryapp:id/minusBtn":
System.out.println("mins");
values.put(InventoryDB_Contract.entries.COLUMN_PRODUCT_QUANTITY_IN_STOCK, quantity - 1);
updatedRow = context.getContentResolver().update(updateItemUri, values, null, null);
break;
case "com.example.android.inventoryapp:id/deleteItemBtn":
System.out.println("delete");
context.getContentResolver().delete(updateItemUri, null, null);
context.finish();
break;
}
}
}
Typecast your activity context into an activity. And then call finish method
Activity act=(Activity)context;
act.finish();
public class DetailActvityActionBtn implements View.OnClickListener
You are not extending Activity or Fragment or anything along those lines, you have no context to execute context.finish(); from because finish() is a method from Activity.
If this class is utilized from an Activity then pass in that activity's reference to the class constructor, like so:
public MainActivity extends Activity{
//You standard onCreate() blah...
DetailActvityActionBtn yourHandlerClass = new DetailActvityActionBtn(this);
}
public class DetailActvityActionBtn implements View.OnClickListener {
private Activity activity;
public DetailActvityActionBtn(Activity activity){
this.activity = activity;
}
#Override
public void onClick(View view){
//You can now call activity.finish() to close the calling activity...
activity.finish();
}
Personally, I would suggest decoupling the button's reliance upon the existence of an activity. Rather than setting the functionality of onClick within the Button class, why not define the functionality within your controller (Activity)? You can define one onClick method and use your listview logic to determine which buttons should have this functionality.
If you were simply controlling business logic that would be one thing, but I personally think it's convoluted to have the view define the controller life cycle. The controller can give permission to the button to dismiss itself, but any other way and the button starts talking to things it probably shouldn't. Maybe you're following a different paradigm than MVC, so I could be wrong!
I was thinking something along the lines of:
#Override
public View getView(int position, View v, ViewGroup parent) {
if(condition1){
v.button.setOnClickListener(locallyDefinedOnClickForCondition1);
}
else if(condition2){
v.button.setOnClickListener(locallyDefinedOnClickForCondition2);
}
}
Would definitely not say it's the best solution, but maybe this could put you in the right direction. Anyone have any criticism?

Android: Getting list of views by tag and changing specific button

I am writing a custom calendar, my calendar is a Grid View that contains a button for each day with a onClick Listener on every button. Each has a tag representing a date.
I am needing to find the button in a layout by specific tag(date) and then work with it (change its contents to bold)
So, first, I am getting all the views that match the tag(event date):
/**
* Get all the views which matches the given Tag recursively
* http://stackoverflow.com/questions/9920559/how-to-find-list-of-views-that-has-a-specific-tag-attribute
* #param root parent view. for e.g. Layouts
* #param tag tag to look for
* #return List of views
*/
public static List<View> findViewWithTagRecursively(ViewGroup root, Object tag){
List<View> allViews = new ArrayList<View>();
final int childCount = root.getChildCount();
for(int i=0; i<childCount; i++){
final View childView = root.getChildAt(i);
if(childView instanceof ViewGroup){
allViews.addAll(findViewWithTagRecursively((ViewGroup)childView, tag));
}
else{
final Object tagView = childView.getTag();
if(tagView != null && tagView.equals(tag))
allViews.add(childView);
}
}
return allViews;
}
Next I am trying to make a button from that view and set its text to bold
Log.d(tag, "calendarFormatdate "+eventDate);
List gridcells = findViewWithTagRecursively(calendarView, eventDate);
if (gridcells.size() == 1) {
Button gridcellButton = gridcells.get(0); //this doesnt work(required Anroid.Widget.Button - FOUND Java.Lang.Object
//Object gridcellbutton = gridcells.get(0); //this works but cannot assign it to Button object
}
I understand that Button and Java Object are different, but it there any way to convert this object to button to use as I am showing? Object gridcellBUtton value is the following:
renaldyalisys D/GridCellAdapter: object value android.widget.Button{eb9b3a4 VFED..C. ........ 0,0-68,72 #7f0c00bc app:id/calendar_day_gridcell}
instanceof
I am not an Android programmer, but it looks like your Question is a basic Java question. Are you saying:
I have an object being returned as the superclass Object. But I know in fact that the object is an instance of the Button class. How can I refer to it as a Button specifically rather than as Object generally?
If so, simply cast the object. Going from the more general super-type to the more specific type is downcasting.
Object o = gridcells.get(0) ;
Button b = ( Button ) o ;
Or combine:
Button b = ( Button ) gridcells.get(0) ;
If not quite sure, do a check with the special operator instanceof.
if( gridcells.get(0) instanceof Button ) {
Button b = ( Button ) gridcells.get(0) ;
}
If you are doing much casting in your code, you likely have a design problem. Usually that stems from not understanding the basics of Object-Oriented Programming. But occasionally some casting is in order and cannot be avoided.
See this similar Question for more discussion.
If I am misunderstanding the Question, you may need to re-write your Question.

android: after async process get data back from onPostExecute to view OR pass view to onPostExecute

i am trying to add extra input fields on the fly to my view.
i first read json string from url with an async funct and map this dynamically to an object with a hasmap with GSON.
next i want to iterate the hashmap to create and add the input fields:
public class NewProductActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.add_product);
TextView view = (TextView) this.findViewById(android.R.id.content);// is this the correct way to get view?
new loadTemplateAttributes(view).execute();// android studio complains about passing view here
.. snip...
class loadTemplateAttributes extends AsyncTask<String, String, HashMap> {
HashMap mymap;
.. snip...
protected void onPostExecute(HashMap mymap) {
pDialog.dismiss();
Iterator it = mymap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
String name = (String) pair.getKey();
TemplateAttribureProperties t = (TemplateAttribureProperties) pair.getValue();
String type = t.getType();
System.out.println("Name=" + name + " type=" + type);
LinearLayout ll = (LinearLayout) findViewById(R.id.linearLayout2);
// add text view
TextView tv = new TextView(this); // 'this' is unknow here
tv.setText(name);
ll.addView(tv);
EditText et = new EditText(view); // 'view' is what i want to pass but dont know how
et.setText();
ll.addView(et);
it.remove();
}
problem is that 'this' is unknown inside onPostExecute function.
i read something about passing the view to the async function but to me it is unclear how to get the view in the firstplace and how to pass it after...
also a lot of options dont seem to work because they are deprecated or are unsafe because the might introduce memory leaks according to the comments.
really lost here.
I don't know what you are doing but You can use Context context; and TextView view; before onCreate() making it global and then you can callTextView tv = new TextView(context); in your method.
public class NewProductActivity extends Activity {
Context context=this;
private TextView view;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.add_product);
view = (TextView) findViewById(android.R.id.content);
}
new loadTemplateAttributes(view).execute();// android studio complains about passing view here
you would have to add constructor to your asynctask, like that:
class loadTemplateAttributes extends AsyncTask<String, String, HashMap> {
View view;
public loadTemplateAttributes(View v) {
view = v;
}
TextView tv = new TextView(this); // 'this' is unknow here
from internal class in java, you refer to parent class using NewProductActivity.this syntax in your case.
EditText et = new EditText(view); // 'view' is what i want to pass but dont know how
et.setText();
you could use aproach with constructor as I described above, or refer directly to activity view: NewProductActivity.this.view. But you would have to make view a class field in your activity.
Advanced: making your activity an internal class instead of static, and also passing views to it, might cause reference leaks and also crashes in case you use view (inside AsyncTask) that was invalidated due to screen rotation. This is especially possible if your AsyncTask is doing network operations. To prevent it always make AsyncTasks static, also if you pass views or activities to them, then wrap those references in WeakReference<>

Android ExpandableListView Where to Place onClickListener

I have an ExpandableListView. Each child has a CheckBox and a TextView. When a user taps a child row, the CheckBox is supposed to change its state (checked vs unchecked). This works correctly if the user taps directly on the check box. However, if the user taps on the text view (same row, immediately to the right of the check box), I get a null pointer error as soon as I try to refer to the check box. Can anyone see what is wrong?
EDIT: After reading the suggestion below, I did some investigation and realized that I can implement my clickListener inside my adapter under getChildView(). This solves my issue with the null pointer as I can easily get a reference to the child view.
However, it creates another issue that I see no elegant solution to. Each time a child is clicked, I need to make changes to the listview itself. The data for this list resides in an ArrayList whose scope is within the Activity (not in the Adapter). If my clickListener is in the Adapter, how can I call back to the Activity to make changes to the ArrayList?
This strikes me as a catch-22. If I want to be able to manipulate my data, I can't get a reference to the child view. But if I want a reference to my child view, my data is out of scope, so I can't manipulate it. How do people resolve this? I must be missing something.
I'll throw in the relevant adapter code where you can see the beginning of my attempt to add a child onClickListener.
Thanks again!
public class Settings extends Activity {
//this is the list I need to access from the adapter if my click listener is there
private ArrayList<Categories> categoriesList = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_smart_settings);
db = new EventsDB(this);
expListGroups = new ArrayList<ExpandListGroup>();
setGroups();
/* Set up the data adapter */
expAdapter = new ExpandListAdapter(Settings.this, expListGroups);
populateExpandableGroups();
}
public void setGroups() {
/* Create lists of the individual line items */
categoriesList = db.getCategoriesForClient(client);
locationsList = db.getLocationsForClient(client);
/* Add an item to the locations list to allow the user to add a new location */
locationsList.add(new ClientSmartFinderLocations(client, "Add Location", null, false));
ExpandListGroup categoryGroup = new ExpandListCategory("Choose Categories", categoriesList);
ExpandListGroup locationGroup = new ExpandListLocation("Choose Locations", locationsList);
expListGroups.add(categoryGroup);
expListGroups.add(locationGroup);
}
private void populateExpandableGroups() {
expandableList = (ExpandableListView) findViewById(R.id.expandable_list);
expandableList.setAdapter(expAdapter);
//I've removed this section and moved it to the adapter, per my edit above
// expandableList.setOnChildClickListener(new OnChildClickListener() {
//
// #Override
// public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
//
// /* Update the finder setting for this client */
// String category = categoriesList.get(childPosition).getCategory();
// Boolean isSelected = !categoriesList.get(childPosition).getIsSelected();
// db.setClientCategory(client, category, isSelected);
//
// /* Update the check box to provide feedback to the user */
// View view = parent.getChildAt(childPosition - parent.getFirstVisiblePosition() + 1);
// CheckBox checkBox = (CheckBox) view.findViewById(R.id.check_box);
//
// //error occurs here
// checkBox.setChecked(!checkBox.isChecked());
// return true;
// }
// });
expAdapter.notifyDataSetChanged();
}
public class ExpandListAdapter extends BaseExpandableListAdapter {
//other methods
#Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View view, final ViewGroup parent) {
view = getCategoryChildView(groupPosition, childPosition, view);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TextView tv = (TextView) v.findViewById(R.id.expand_list_item);
CheckBox checkBox = (CheckBox) v.findViewById(R.id.check_box);
String category = tv.getText().toString();
Boolean isSelected = !checkBox.isSelected();
db = new EventsDB(context);
db.setClientCategory(client, category, isSelected);
checkBox.setChecked(!checkBox.isChecked());
//here I need to do some things that require me to manipulate the categoriesList from the Activity class - but it is out of scope
}
});
return view;
}
}
<CheckBox
android:id="#+id/check_box"
android:layout_marginLeft="30dp"
android:focusable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/expand_list_item"
android:paddingLeft="10dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="#dimen/smart_finder_settings_font_size"
android:textColor="#FFFFFF" />
I think that I have a solution. If I create an instance of the Settings activity class within the Adapter, I can add the usual getter/setter methods to the Settings class. I can then call these methods from the adapter to get at that list, modify it, and then push it back to the Settings class, and all should be well.
It's a basic OOP approach, but the mental hangup for me was the idea of instantiating an activity. I don't know if this is common or not, but it seems weird to me. Anyone have a more elegant solution?
Within the Settings class:
public ArrayList<Categories> getCategoriesList() {
return categoriesList;
}
public void setCategoriesList(ArrayList<Categories> list) {
categoriesList = list;
}
Within the adapter:
Settings settings = new Settings();
ArrayList<Categories> tempCategoriesList = new ArrayList<Categories>();
tempCategoriesList = settings.getCategoriesList();
//make changes to the list
settings.setCategoriesList(tempCategoriesList);
You should set the checkbox checked state inside your ExpandListAdapter instead of accessing the view inside the ExpandableListView directly.
Im not a pro, so take that in mind. but what about storing the data you need the clicklistener to manipulate in a gobal application class? Maybe not the most elegant, but might work.
user55410 is on the right track. In your Settings() Activity, make categoryList static, then when you change it with the getter/setter methods from the new instance you create in your it will modify all instances of Settings.categoryList.

Categories

Resources