How to handle clicks / touches on repeated custom components? - android

So, I have a screen that has from 0 to 6 of the same user interface components included via XML. Something similar to this:
<TableRow android:layout_height="150px">
<include android:id="#+id/p1" android:layout_width="fill_parent"
android:layout_height="wrap_content" layout="#layout/numerics_component"
android:onClick="onClick" />
</TableRow>
<TableRow android:layout_height="150px">
<include android:id="#+id/p2" android:layout_width="fill_parent"
android:layout_height="wrap_content" layout="#layout/numerics_component"
android:layout_below="#id/p1" android:onClick="onClick" />
</TableRow>
... etc
Each of those included UI's is a collection of several widgets and custom components that I am reusing.
I want to detect clicks on components in those included bits in my Activity and respond appropriately. The problem is, in my onClick method if I follow the common pattern, I can never tell which of the views got a click:
public void onClick(View view) {
Log.d(loggingName, "Got onClick event on view: " + view);
// Identify the view, and handle appropriately:
switch (view.getId()) {
...
With the above code I can never tell which of the 6 copies of the component got clicked. There must be a good way to do this, but I am not seeing it.
Further, I don't want to hard code the reusable component to one activity because I want to reuse it throughout multiple activities in my app. So ideally, I'd be able to setup the listeners in my Activity.
Any ideas on how I can do this?
Thanks!

You could search the hierarchy to figure out which sub-component has been clicked. I see that each row of your table has a unique id. Thus, even though the layout is being repeated with identical ids, the view hierarchy does not have identical ids in any sub-tree.
Thus, to identify which sub-component has been clicked obtain the id of the parent view (which in your case is a row) and see which one of the 6 rows the view belongs to. Does that make sense?

1) The Android View class allows you to tag each instance of a view via the setTag method.
So, just setTag() on each view with some unique Integer, or even perhaps an object with a method that you will invoke. Then in your click listener, just do a getTag() to differentiate between the various view instances.
2) You can put a unique onClickListener on each of the view instances.
3) You could do a findViewById() on each of the view instances, and store them in member variables, an array, or some other data structure. Then in your onClickListener, you simply compare the View reference passed to onClick() against your list of saved references.

Related

Giving id's to dynamically created views

I'm a beginner in coding, and I would love some help. I want to make an alarm application. On my main page fragment, I added a button that will add an alarm into a LinearLayout inside a ScrollView. The alarm will have three TextViews in it, and a button for activation/deactivation.
Here is how I would like my alarm to look like (this is currently not being used anywhere in my coding; I created it just to have a visual aid of what I'm aiming to make):
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:id="#+id/alarm_fl"
android:background="#mipmap/white_box">
<Button
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="9.5dp"
android:id="#+id/alarm_activation_button"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="70dp"
android:layout_marginTop="5dp"
android:textSize="10pt"
android:id="#+id/alarm_time"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="165dp"
android:layout_marginTop="11.5dp"
android:textSize="7pt"
android:id="#+id/alarm_ampm"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="70dp"
android:layout_marginTop="30dp"
android:textSize="5pt"
android:id="#+id/alarm_day"/>
</FrameLayout>
This is how I'm currently testing my alarms in the fragment:
addAlarm.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
LayoutInflater alarm_inflater = (LayoutInflater) getContext().getSystemService(LAYOUT_INFLATER_SERVICE);
ViewGroup parent = (ViewGroup) getActivity().findViewById(R.id.alarm_ll);
View alarm_view = alarm_inflater.inflate(R.layout.alarm_layout, parent);
TextView alarm_time = (TextView) alarm_view.findViewById(R.id.alarm_time);
alarm_time.setText("9시 45분");
TextView alarm_ampm = (TextView) alarm_view.findViewById(R.id.alarm_ampm);
alarm_ampm.setText("오후");
TextView alarm_day = (TextView) alarm_view.findViewById(R.id.alarm_day);
alarm_day.setText("월,화,수,목,금");
Button activation_button = (Button) alarm_view.findViewById(R.id.alarm_activation_button);
activation_button.setBackgroundResource(R.mipmap.checkbox_deactivated);
}
});
where alarm_ll is the LinearLayout that I want to populate with newly created alarms.
And it appeared to me that I need unique id's for each of the Buttons and TextViews to manipulate them separately.
Now here are my questions:
Is this the right approach if I want to add views programmatically whenever the button is clicked? Or is there a better, simpler way?
My alarms would eventually need to be objects. Would it be possible for a non-activity class like User, or in this case Alarm, to have a layout for it's own?
How do I give unique id's to each view when creating via a button click?
When I test-run my application now, the layouts I add into alarm_ll won't be saved, so if I shift to another activity and come back, alarm_ll will be reset. How do I save these in a fragment, when they are not in primitive data types?
I'm sorry to ask so many questions at once, but I would really love some answers or suggestions. Thank you.
I assume that you want to have the ability to set multiple alarms. This is a perfect use of ListView or RecyclerView which allows you to inflate several copies of the same view and display a list based on some underlying data.
I suggest that you learn about creating "model" which are objects that store the data. These "model" objects should be separate from the view objects which display the data. There are several design patterns which are commonly used for this kind of separation: Model-View-Controller, Model-View-Presenter, and Model-View-ModelView.
android:id in XML is typically used to be able to get an object for the view in the Java code. When you create a view dynamically, either the way you showed in your question or by inflating XML, you already have this object, so I do not see a need for assigning an ID to these dynamically created views.
When using one of the design patterns which I suggested in #2, you will also create a way to store the data. Android provides an API to store information in a database. This can easily be displayed in a ListView or RecyclerView by using an "adapter".

Accessing a view that has a shared ID within shared layouts when only one layout is visible

In my Android app I have a layout for an activity that presents a choice.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/wrapper"
android:orientation="vertical">
<include layout="#layout/choices" />
<include layout="#layout/choice_one" />
<include layout="#layout/choice_two" />
</LinearLayout>
In layout/choices the user sees two buttons. One button shows layout/choice_one and the other shows layout/choice_two. (The parent in layout/choice_one and layout/choice_two is initially set to android:visibility=gone.)
So when a user chooses either choice_one or choice_two, essentially I'm setting the visibility to VISIBLE. That works great.
The issue is that inside of both choice_one and choice_two I have shared elements with the same ID. For example I have a TextView with ID header. (I did this because I figured only one of those layouts would be visible and they use the same things.)
The issue is that I use Butterknife, and it seems like if I Bind header, when I set the visibility on choice_one or choice_two, I have a 50/50 chance of correctly calling header.setText("Blah") on the appropriate header element.
I'm sure I can get around this by giving unique IDs to all elements in the layouts or ditching Butterknife and using findViewById instead. But is there another way I can target a shared ID inside of a layout without ditching Butterknife or my shared layouts?
I figured this out. (Writing it out helped, so I'll keep it here in case someone else stumbles upon it.) Instead of using include in xml and inflating layouts there, I just subclassed ViewGroup for my two layouts and added them to wrapper:
ChoiceOne choiceOne = new ChoiceOne();
wrapper.addView(choiceOne);

FragmentManager multiple on-screen fragments?

On Android 3.0 and above, the android team is driving hard that you should use fragments over activities. And I see this being useful, but I want to be able to handle click events in my app. I'm using a list fragment on the right side of my app, so doing an onclick (or any click listeners) happens in the activity that hosts the fragment. So I had to move from putting a item in XML to using the fragment manager.
In the design documents they show this picture:
http://developer.android.com/training/basics/fragments/fragment-ui.html
What I want is the Fragment A/B tablet UI. However, nowhere in this page does it actually give you an example of doing this - it seems that fragment manager only works with ONE fragment at a time - which is entirely opposite of what the picture portrays. Which makes me think it uses in XML... but then how would I get an onclick? These documents don't make a lot of sense to me. It shows one thing and then says something else. What if I wanted to remove fragment A on the tablet? Add fragment C that doesn't yet exist? Is that even possible if you tried to use Fragment Manager????
I guess I don't get if Fragment manager uses more than 1 fragment, and if it does, how am I supposed to use this to get an item in the picture like the tablet - the left (A) being a listview, and the right (B) being whatever. Without an ID of the fragment I don't how to access it.
Not sure if this is relevant but here is some of my code:
Adds a fragment to the single framelayout I made like in the guide
//Activity
FragLaunch launchPane = new FragLaunch();
// In case this activity was started with special instructions from an Intent,
// pass the Intent's extras to the fragment as arguments
// firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getFragmentManager().beginTransaction()
.add(R.id.launch_frag_container, launchPane).commit();
}
Also, in portrait mode of 7" tablets, I want it to use a viewpager that is swipeable. It worked like a charm when I designed it in XML but now that I have to access the listfragment it doesn't work (no way to access since I can't have two fragments)
XML of FragLaunch's content view:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:gravity="center_vertical"
android:orientation="vertical" >
<TextView
android:id="#+id/initial_directions"
style="#style/textScalar.Roboto.Light"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="#string/initial_directions"
android:layout_marginBottom="30dp"
tools:context=".Launch" />
</LinearLayout>
I want to have this one appear as Fragment A in the photo:
FragHistory.java/xml for fragment:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="#+id/spamhistory"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Spam History" />
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:drawSelectorOnTop="false" />
</LinearLayout>
Does anyone have any insight on this?
If you want your fragments to be able to communicate then you need to use interfaces, like this.
For onClick events you simply set an onClickListener for the view that you need to receive the onClick event from. Like so:
sampleView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Your code here
}
});
As for fragment transactions, it says somewhere in there (I can't remember exactly where) that when two fragments are displayed on the screen at once (as with larger screens) that instead of swapping the fragments it simply updates it. All you have to worry about is making the correct calls. So if you want to remove fragment A just call remove(). Or if you want to replace it with fragment C call replace() and pass fragment C in as the parameter.
Can you clarify your question about the ViewPager? What do you mean "have to access it"?
Hope this helps!
EDIT: I apoplogize, I misunderstood your question. Here's kind of a quick run down of how to add more than one fragment to the screen at once.
1. Perform a runtime check to make sure that the device screen is big enough to display more than one fragment.
2. If the screen is big enough, set the content view to use a layout that has a FrameLayout for each fragment that you want to add.
3. After that grab a reference to each fragment that you want to use.
4. Then use the FragmentManager to add a fragment to each layout. Like this:
FirstExampleFragment fragment1 = new FirstExampleFragment();
SecondExampleFragment fragment2 = new SecondExampleFragment();
getSupportFragmentManager().beginTransaction().add(R.id.example_framelayout1, fragment1)
.add(R.id.example_framelayout2, fragment2).commit();
Another great way to allow communication between fragments is to use an event bus, such as the Otto event bus. Otto allows components to publish events and subscribe to events in a decoupled manner.
In your particular case, when a user selects an item in the list, your list fragment can publish an event (which can include the item that has been selected) and your content fragment can subscribe for these events and update its content accordingly when it receives a new event. This all being done without the two fragments being directly coupled and without having to define additional interfaces.
I know this doesn't answer your entire question, but thought it might be useful when it comes to the communications between your fragments....YMMV.

Issue regarding include tag in xml and implementation of the views

Hi all and thank you in advance and sorry for my english. I have two big doubts
1 - I haven't been too much time programming in android, and i pretty sure there is a lot of things i have made in wrong ways. For example, I have made several apps where in xml definition i include another xml.
For example, imagine 2 activities with a header_section.xml being include in both activities xml definition. That header_section has 5 buttons and more views etc. Ok, in the xml is just make an include and it works......but to implement the buttons.....do i have to REPEAT the code in both activities?? it sounds really bad practice to duplicate code in both activities.....but how can i do, for example this in activities A and B? Do i have to put this code exactly the same in both activities classes????
private View header_section;
private Button bExample;
header_section=findViewById(R.id.header_section);
bExample=(Button)header_section.findViewById(R.id.bExample);
bExample.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//Whatwever...call a number, for example
}
});
In main xml something like:
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="0dp" >
<include android:id="#+id/header_section" android:layout_gravity="center" android:gravity="center" layout="#layout/header_section" />
</LinearLayout>
and in header_section.xml something like:
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="0dp" >
<Button android:id="#+id/bExample" />
</LinearLayout>
2 - Imagine you have like 10 activities in your app. If in all of them there are a header section and bottom section with the same functionality, changing just the central area (showing different lists, views etc)......is better have just one activitie in ALL the app, with a viewflipper in the central area? or have 10 activities having, i do not know if can be avoided, asked in point 1, code repeated in all 10 activities for the implementation of the headers and bottoms views, handlers etc?
Thanks and best regards
1) Yes, usually, you should have to use them BUT you can make it simplier...
1.a) bExample=(Button)findViewById(R.id.bExample); //don't need to load the View
1.b) You can shorten a bit how you call the onclick, in your button/clicable element inside the LAYOUT, here's an example:
<!--inside layout -->
<Button android:id="#+id/bExample" android:onClick="aceptar" />
and
//inside the Activity
public void aceptar(View v){
//here the code of the button
}
For the question about implementing the same methods inside all Activities, check this post: Adding the same context menu to multiple activities
2) Depending the application
If you don't do much, you can load all in the same activity and HIDE/SHOW the layout elements you don't want.
But it's better to use different activities, anyway, if the layout is not "heavy" (too many elements/includes inside) you can load the SAME layout for all your activites, and you only need to change the different contents (strings) and/or HIDE/SHOW the different elements.

Sharing data with custom canvas view

I'm a beginner at android programming, so excuse me if my wording is slightly incorrect.
I have a custom canvas view along with a TextView inside a linear layout, defined in the layout file as
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="#+id/blah"
android:layout_width="fill_parent"
android:layout_height="60dp"
android:gravity="top|center_horizontal"
/>
<com.*.Overlay android:id="#+id/combined"
android:layout_width="fill_parent"
android:layout_height="100dp"
/>
</LinearLayout>
I need to be able to read the text contained in the TextView from within the Overlay custom class that I created.
(The overlay class takes in 2 bitmaps and puts one on top of the other. The bitmaps used will depend on the text in the TextView.)
I considered using intents, but the Overlay class doesn't have an onCreate method.. All my code is within the onDraw method. I also added the necessary constructors.
I'm not sure what to try next, perhaps try accessing the parent linearlayout and then its child textview?
Hope I managed to explain everything in a non-confusing manner
Ok, managed to fix the issue... sort of
I found out that Views need to be contained in Activities.. so I created a new Activity with my custom view as an inner class, passed an intent with the necessary data to the activity and was able to use it successfully in my custom canvas view.
I was a bit surprised I didn't get any responses, but I guess that's because I'm new here

Categories

Resources