I'm developing an Android 3.1 application that uses fragments.
On one of those fragments a need to create n buttons and set an onClick event handler for each of them.
To do it I want to create a method on FragmentActivity that handles those events but I don't know how. Note: FragmentActivity is a android.support.v4.app.FragmentActivity that manages all fragments using android.support.v4.view.ViewPager.
On another fragment I have the following XMLcode:
<Button
android:id="#+id/btnTakeArticlePhotos"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="#string/btn_take_photo"
android:onClick="onTakePhotoClick" />
And this code on FragmentActivity:
public void onTakePhotoClick(View view)
{
Log.v("FillEReportFragmentActivity", "onTakeFactoryPhotoClick");
int imgType, imgSubType;
switch (view.getId())
{
case R.id.btnTakeFactoryPhotos:
imgType = ImageType.EREPORT;
imgSubType = SubImageType.EREPORT_FACTORY_OUTLOOK;
break;
case R.id.btnTakeArticlePhotos:
imgType = ImageType.ARTICLE;
imgSubType = SubImageType.NONSET;
default:
imgType = -1;
imgSubType = -1;
break;
}
Intent intent = new Intent(FillEReportFragmentActivity.this, CameraActivity.class);
intent.putExtra(BundleKeys.tablePk, eReportId);
intent.putExtra(BundleKeys.imgType, imgType);
intent.putExtra(BundleKeys.imgSubType, imgSubType);
startActivityForResult(intent, CAMERA_REQUEST_CODE);
}
I want to do the same with these n buttons: create a method on FragmentActivity to handle all onClick events.
I see that if I want to handle onClick event on a button created programmatically I need to implement onClickEventListener.
How can I handle those onClick events on FragmentActivity? or is there a better approach?
You should make your FragmentActivity implement View.OnClickListener.
Then in your Fragment in the onActivityCreated() callback you can do the following :
getView().findViewById(R.id.Button1).setOnClickListener(
(OnClickListener)getActivity));
You could also define your own interface and make your Activity implement that interface and the same in onActivityCreated(), and let the Fragment implement OnClickListener and then call your Activity like this :
public MyFragment extends Fragment implements OnClickListener(){
public MyInterface mInterface;
protected void onActiviyCreated(){
mInterface=(MyInterface)getActivity();
}
public void onViewCreated(View view, Bundle savedInstanceState){
view.findViewById(R.id.myButton).setOnClickListener(this);
}
public void onClick(View v){
....
mInterface.buttonClicked();
}
}
Related
I have a rather simple screen that only has 4 buttons. I'm implementing it as a Fragment like so:
public class MainFragment extends Fragment implements View.OnClickListener {
// ...
#Override
public void onClick(View view) {}
}
Each button already has onClick specified to a function in the Activity that the Fragment is attached. The issue I'm having is that the onClick functions aren't called when the buttons are clicked. I've left MainFragment.onClick() empty - but is that the right approach? Does it need to be implemented for the functions to be invoked? If so, the onClick attributes in the Button layouts would seem redundant.
Any help will be appreciated.
Thanks
The right approach is to use a fragment listener to communicate back with the activity:
public static class MainActivity extends Activity
implements MainFragment.onFragmentInteraction{
...
public void onFragmentInteraction() {
// Do something
callFunction();
}
}
Then in your fragment:
mYourButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
if (mListener != null) {
mListener.onFragmentInteraction();
}
}
});
FWIW I never use the xml onClick attributes. Although they may save a couple of lines of typing, they make it more difficult to follow what's happening in your code.
If your class implements View.OnClickListener and you have correctly overriden the onClick method (which it looks like you have), then you can safely remove any onClicks in your layout files and instead assign methods to your widget clicks in the following way:
public class MainFragment extends Fragment implements View.OnClickListener {
private Button viewOne, viewTwo, viewThree;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.your_layout, container, false);
viewOne = (Button) rootView.findViewById(R.id.view_one);
viewTwo = //etc...
//"this" refers to the current object. As the object is of a class that implements OnClickListener,
//passing "this" satisfies the View.OnClickListener parameter required for the setOnClickListener() method.
viewOne.setOnClickListener(this);
viewTwo.setOnClickListener(this);
viewThree.setOnClickListener(this);
return rootView;
}
#Override
public void onClick(View view) {
//To identify the correct widget, use the getId() method on the view argument
int id = view.getId();
switch (id) {
case R.id.view_one:
//viewOne clicked
break;
case R.id.view_two:
//And so on...
}
}
}
If you set the onClick in your XML, the click events will go to your container Activity. But you can have the click events go directly to your Fragment by setting the onClickListener to your Fragment's implementation of it. So in your Fragment's onCreateView() method, you would inflate your layout, then set the Button's onClickListener to your Fragment's implementation like this...
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.your_fragment, container, false);
Button button = (Button) view.findViewById(R.id.your_button);
button.setOnClickListener(this);
return view;
}
By setting the setOnClickListener() to this, you are sending all click events for that button to your Fragment instead of your Activity. Then you would just handle your onClick events as you're already doing...
#Override
public void onClick(View view) {
Log.d("YOUR BUTTON", "This is called from your Fragment instead of your Activity");
}
I have a FAB in my activity_main and I have 5 ViewPager fragments.Fragments have RecyclerView. How do I access this RecyclerView from Main activity and set on click method for FAB so that on clicking FAB, recyclerview in the active fragment scrolls to top.I tried using mRecyclerView.scrollToPosition(0) inside fragment.But it doesn't work for all fragments.I have this inside fragment.Should I place the following in main activity.java? How to access RecyclerView and scroll it to the position 0 on clicking FAB in a fragment?
public void setFloatingActionButton(){
fab = (android.support.design.widget.FloatingActionButton) getActivity().findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mRecyclerView.scrollToPosition(0);
}
});
}
Simplest and easiest solution is to use EventBus.
Two best EventBus libraries for Android are EventBus and Otto. I use otto.
All you need to do is register the bus in your fragment where recycle view is placed. And write a method e.g
public void scrollToStart(){
//body here.
}
Subscribe the above method to the bus like this.
#Subscribe // subscribing an event to the bus.
public void scrollToStart(ScrollToStartEvent event){ //you need to create an event class e.g ScrollToStartEvent
//body here.
}
Now FAB onClick Listner.
public void setFloatingActionButton(){
fab = (android.support.design.widget.FloatingActionButton)
getActivity().findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
BUS.post(new ScrollToStartEvent()); //NOTICE
}
});
}
Note : All I did is posted an event, in your case ScrollToStartEvent(). Bus will check if any method is subscribed to this event and will call that particular method. You've to read EventBus documentation to understand this correctly.
Pros : Much less code and more readable code. Complex objects can be passed through (no object serialization is required).
Cons : You'll need to learn how to use EventBus.
I hope it helps you.
I'm not sure if this will help but I set up the FAB onClick in the activity and here is how I did it. I have conditional actions based on which fragment position is being viewed.
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.fab:
if(viewPager.getCurrentItem()==0){
Toast.makeText(this,"First",Toast.LENGTH_LONG).show();
}
else{
Toast.makeText(this,"Second",Toast.LENGTH_LONG).show();
}
}
}
In my example I only have 2 fragments so I hard coded the 0 and did an if else. In your case I would use a switch within the switch. Also a good way to organize all the fragments would be with a private SparseArray<WeakReference<Fragment>> fragmentMap = new SparseArray<WeakReference<Fragment>>();
you would include this:
#Override
public void onAttachFragment(Fragment fragment) {
Bundle bundle = fragment.getArguments();
if(bundle!=null && bundle.containsKey(KEY_FRAGMENT_POSITION)){
int position = bundle.getInt(KEY_FRAGMENT_POSITION);
fragmentMap.put(position, new WeakReference<Fragment>(fragment));
}
super.onAttachFragment(fragment);
}
public Fragment getFragment(int type){
WeakReference<Fragment> weakFrag = fragmentMap.get(type);
Fragment frag = null;
if (weakFrag != null)
frag = weakFrag.get();
return frag;
}
Sometimes when an Activity event gets lost in the chain due to the way that XML or events are structured the bellow is the best structure to ensure the events go to the right places.
public class MainActivity extends AppCompatActivity implements View.OnClickListener
{
#Override
public void onClick(View v) {
Log.v(LOG_HEADER,"onClick");
}
}
public class SearchFragment extends Fragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
LinearLayout ll = (LinearLayout )inflater.inflate(R.layout.fragment_search, container, false);
ll.setOnClickListener((MainActivity)getActivity());
return ll;
}
}
I am learning how to use ViewPager in my android app. Right now, I was able to create a basic app with three tabs with swipe support. Now, what I'm thinking to do is, how can I go from one page to another using a button inside one of the fragments currently in the ViewPager, I have tried this one
Android ViewPager Prev/Next Button
but it does not work for me, for a better understanding of what I am trying to achieve:
I have three(3) tabs (3 Fragments),
TAB A,
TAB B,
TAB C
in TAB A, I have a button. I want the action of that button INSIDE TAB A(Fragment A) to go to TAB C (or TAB B). I've been searching for the right approach but to no avail. I hope someone can help me, Thanks!
okay here is how to add button in fragment A to switch fragment c :
1- add button in Fragment a in xml file which id foo like this :
<Button
android:id="#+id/foo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="go to fragemnt c" />
go to fragment a class overide method called onCreateView and set onclickListner create by creating interface called it buttonClick and create var from it in side fragment a then override method onAttch initialize interface var inside onAttch method:
public class FragmentA extends Fragment {
buttonClick click;
Button foo;
interface buttonClick {
void buttonClicked(View v);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
click = (buttonClick) activity;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_a, container,false);
foo = (Button) view.findViewById(R.id.foo);
foo.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
click.buttonClicked(v);
}
});
return view;
}
}
then back to your MainActivity implements buttonClick interface and override methods in the method called buttonClicked(View v); setCurrentItem for view pager like this :
calss MainActivity implements FragmentA.buttonClick {
// your code here ...
public void buttonClicked(View v){
//get your viewPager var
viewPager.setCurrentItem(3);
}
}
i hope this helps
Get your ViewPager, use
ViewPager.setCurrentItem(int item)
I have a fragment which has a TextView, an EditText and a Button. I also have 2 activities which include this fragment and at onClick of the button in one of the activities, the other is started. Via the intent, the text in the edittext is passed which becomes the text of the textview of the other activity.
I had two design decisions to choose from
Create two such fragments classes with appropriate methods that construct the appropriate intents. Access the UI elements from inside the respective fragment object and start the activities.
Create only one fragment class. onClick the, event is passed down to a particular method in the activities (both the activities have this method) and the activities have the logic to build the intent and start the other activity
Consider what would happen if there are 100 such activities. The first method would have us write 100 different fragment classes with custom methods, but in the second method, it is a single class and the activities have the custom logic in a particularly named method.
Therefore I chose to go with the second choice and I realized that the UI elements could not be instantiated in the onCreate method of activity as the fragment's layout is not inflated yet. I am doing the instantiation in onStart as a workaround.
Is that bad practice or is there a better design pattern to follow?
The recommended pattern is to create a holder interface which any activity that wants to instantiate your fragment must implement. Also to set data for views in your new fragment then create a newInstance() factory method on your fragment.
I tend to approach it like this;
class FooFragment implements Fragment {
private static final String TEXT_FOR_TEXTVIEW = "textForTextView";
private FooFragmentHolder mHolder;
/*
* Rather than creating your fragment in your layout directly
* you should instead instantiate it using this class in your
* activity.
*/
public static FooFragment newInstance(String text) {
Bundle data = new Bundle();
data.putString(TEXT_FOR_TEXTVIEW, text);
FooFragment fooFragment = new FooFragment();
fooFragment.setArguments(data);
return fooFragment;
}
public interface FooFragmentHolder {
public void buttonPressed(String editTextContent);
}
/*
* When we create the fragment with the activity we use onAttach to get
* our holder implementation (the activity)
*/
#Override
public void onAttach(Activity activity) {
if (activity instanceof FooFragmentHolder) {
mHolder = (FooFragmentHolder) activity;
} else {
throw new IllegalStateException("Containing activity must implement FooFragmentHolder");
}
}
#Override
public void onCreateView(Inflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_foo, container, false);
final EditText editText = (EditText) view.findViewById(R.id.edit_text);
Button button = (Button) view.findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(Button button) {
mHolder.buttonPressed(editText.getText());
}
})};
TextView textView = (TextView) view.findViewById(R.id.text_view);
Bundle args = getArguments();
if (args != null) {
textView.setText(args.getString(TEXT_FOR_TEXTVIEW));
}
return view;
}
}
Now in your activity you just need to implement the FooFragmentHolder interface and use the newInstance method we created;
class FooActivity extends Activity implements FooFragment.FooFragmentHolder {
private static final String TEXT_FOR_TEXTVIEW = "textForTextView";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentLayout(R.layout.activity_foo);
// Instead of creating your fragment in your layout, create a holder
// layout and attach a new instance of your fragment using a fragment
// transaction.
FooFragment fooFragment = FooFragment.newInstance(getIntent().getStringExtra(TEXT_FOR_TEXTVIEW));
getFragmentManager().beginTransaction()
.replace(R.id.content, fooFragment)
.commit();
}
#Override
public void buttonPressed(String editTextContent) {
// In this case just starting the next FooActivity, but logic could be
// applied for any other activity.
Intent intent = new Intent(this, FooActivity.class)
.putExtra(TEXT_FOR_TEXTVIEW, editTextContent);
startActivity(intent);
}
}
I decided to settle with the following patter --
Any activity which includes this fragment should implement an interface like
public interface ViewsCreatedListener {
public void onViewsCreated();
}
The activity would then look like
public class ExampleActivity extends Activity implements ViewsCreatedListener {
.
.
.
.
#Override
public void onViewsCreated() {
//Initiate the views here and do what gotta be done
}
}
The fragment should check that any activity that includes this fragment should implement that interface using the onAttach method and onActivityCreated, the activity is notified
public class ExampleFragment extends Fragment {
ViewsCreatedListener listener = null;
.
.
.
.
#Override
public onAttach(Activity activity) {
super.onAttach(activity);
try {
listener = (ViewsCreatedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement ViewsCreatedListener");
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
listener.onViewsCreated();
}
}
Doing this way, the fragment just provides the UI and the including activities decide as to what should be done with the UI elements included via the fragment. This maximizes reusability.. DRY... :-D
I got 4 activites that all include a xml-footer which contains 4 buttons (one for each activity).
I would now like to setup onclicklisteners to these buttons (it's a self made menu in the footer).
The question is, how do I use listeners so that I can reuse code?
I have two ideas:
Create a class that implements onclicklistener and in every activity i would get the buttons and then create a new instance of the listener class and do button.setOnClickListener(onClickListener)
The problem is that in the listener class, how would i check which button called the event?
And how would I create an intent to start an activity, usually i would do:
Intent intent = new Intent(FromActivity.this, ToAcitivty.class)
But i don't have the reference to FromActivity.
Create a base class that extends from activity and then the 4 activies will extend from the base class. I would then like to setup the listeners in the base class. The problem here is that i can't get the references to the buttons by doing
Button button1 = (Button)findViewById(R.id.menu_button1);
button1 will be null. I haven't even called setEventView because this should be done in the activity not in the base class.
Any ideas?
Thank you
Same code is here:
public class MyClass extends Activity implements View.OnClickListener{
btnA=(Button)findViewById(R.id.btnA);
btnA.setOnClickListener(this);
btnB=(Button)findViewById(R.id.btnB);
btnB.setOnClickListener(this);
}
#Override
public void onClick(View v)
{
Button clickedButton = (Button) v;
switch (clickedButton.getId())
{
case R.id.btnA:
Intent regIntent = new Intent(Home.this,Registration.class);
startActivityIfNeeded(regIntent, 1);
break;
case R.id.btnB:
//Some code
break;
}
}
(edited as the original first line is broken on code format.