I have a main layout that contains a GridView and Buttons.
NOTE BEFORE YOU READ: The reason I am using fragments is because I am displaying lots of images in a GridView at the same time, so I've set up Fragment classes to handle them efficiently without it effecting performance.
I would like the fragments that handle the GridViews behavior to be separate from the activity that handles the buttons behavior(my main fragment and activity both share the same layout).
When I try to do this, the app load up, loads all the images and the button is present.
When I click the button(I've set up a TAG to it) no TAG message shows in console that I have pressed the button.
Then, when I press the back button on my phone the GridView disappears and the button is only present, then once I click it, the TAG message shows the message in the console.
How would I go about fixing this issue, also, what I am doing here, is it a good or a bad idea to do? Thanks in advance.
XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<GridView
android:id="#+id/gridView"
style="#style/PhotoGridLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:columnWidth="#dimen/image_thumbnail_size"
android:horizontalSpacing="#dimen/image_thumbnail_spacing"
android:numColumns="auto_fit"
android:stretchMode="columnWidth"
android:verticalSpacing="#dimen/image_thumbnail_spacing" >
</GridView>
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="Button" />
</RelativeLayout>
Main Activity:
public class MainActivity extends Activity {
public static String TAG = "Test";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image_grid_fragment);
startActivity(new Intent(this, ImageGridActivity.class));
Button SearchListButton = (Button) findViewById(R.id.button1);
SearchListButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "clicked");
}
});
}
}
Main Fragment:
public class ImageGridActivity extends FragmentActivity {
private static final String TAG = "ImageGridActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
if (BuildConfig.DEBUG) {
Utils.enableStrictMode();
}
super.onCreate(savedInstanceState);
if (getSupportFragmentManager().findFragmentByTag(TAG) == null) {
final FragmentTransaction ft = getSupportFragmentManager()
.beginTransaction();
ft.add(android.R.id.content, new ImageGridFragment(), TAG);
ft.commit();
}
}
}
You are starting ImageGridActivity in MainActivity's onCreate. Which means, as soon as the MainACtivity is started, it launches ImageGridActivity.
The SearchListButton is in the MainActivity but ImageGridActivity is on top of the activities stack. This is the reason why the button click is not happening. MainActivity has lost focus and moved to the next position in the activity stack.
When you press back, ImageGridActivity is destroyed and MainActivity is in focus. Now SeachListButton click works.
What is suggest is,
you can start ImageGridActivity on some events like button click. Why do you need two activities? Why not just ImageGrdActivity?
Related
i am trying to hide/show Button in fragment from Activity but it give me following exception.
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Home Activity
public class HomeActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
CategoryFragment frag=(CategoryFragment) activity.getSupportFragmentManager()
.findFragmentByTag("cat_frag");
Button newDesigns= (Button) frag.getView().findViewById(R.id.new_designs);
newDesigns.setVisibility(View.VISIBLE);
}
}
Category Fragment
public class CategoryFragment extends Fragment{
Button newDesigns;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_category, null);
newDesigns= (Button) v.findViewById(R.id.new_designs);
}
}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#CCCCCC">
<TextView
android:id="#+id/list_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:background="#drawable/shape_logo_bg"
android:gravity="center"
android:padding="5dp"
android:textColor="#android:color/white"
android:textSize="18sp" />
<Button
android:id="#+id/new_designs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/list_name"
android:background="#color/edit_button_color"
android:padding="10dp"
android:text="#string/new_designs"
android:textColor="#color/btn_text_color"
android:layout_centerHorizontal="true"
android:layout_marginTop="5dp"
android:visibility="gone"
/>
</RelativeLayout>
Code is too large to be posted here. Thats why i have only posted the code where i am finding an issue.
I am able to get newDesigns BUTTON instance.What is shocking for me is if try to play with button instance (VISIBLE/GONE) it gives me above mentioned exception.
Help is appreciated.
You shouldn't play with a view of the Fragment while you are in a Activity directly. You will not know what the view's state will be and this can potentially lead to issues you can't even think of(beleive me i faced many). To interact with the activity's views, make an interface:
public interface AccessFragmentViews{
public void setVisibilityForButton(boolean bool);
//any other methods that you need
}
Now implement this inside the fragment class and override the method.
class YourFragment implements AccessFragmentViews{
.
.
public void serVisibilityForButton(boolean shouldHide){
if(shouldHide){
yourButton.setVisibility(View.GONE);
}else{
yourButton.setVisibility(View.VISIBLE);
}
}
}
Now you can interact safely with the views of the fragment within a activity using this interface. Make sure that the fragment is alive before doing so though ;) accessing child's view are prone to WindowLeakedExceptions and illegalstateexceptions
In the activity use it as follows:
you can get the fragment reference by either finding it by its tag or by using the reference you used to create the fragment
//NOTE: it is very very dangerous to do the accessing on a fragment views from an activity
//first the alive check then the logic
if(yourFragmentReference!=null){
((AccessFragmentViews)yourFragmentReference).setVisibilityForButton(true);// or false if you want to make it visible
}
Add a method in your fragment class
public void changeButtonVisibility(boolean visibility){
if(visibility){
newDesigns.setVisibility(View.VISIBLE);
}else{
newDesigns.setVisibilty(View.GONE);
}
}
and in your activity class
add this line
public class HomeActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
CategoryFragment frag=(CategoryFragment) activity.getSupportFragmentManager()
.findFragmentByTag("cat_frag");
frag.changeButtonVisibility(true);
}
}
Is it possible to have a button in the main activity and when the user clicks
it , the fragment layout will appear? Im new to Android Studio.If its possible
do you think you can show me an example of it.
It should be similar to the description provided here in the link just add the button to the activity instead of the fragment in this example
Button Click inside Fragment to open up new fragment
You can use it directly from the main activity like this example:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
>
<Button
android:id="#+id/button"
android:layout_width="100dp"
android:layout_height="50dp"
android:text="Click me"
/>
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.root_layout, new NewFragment());
transaction.commit();
//button.setVisibility(View.GONE);
}
});
}
}
Where NewFragment is just a black fragment.
The problem is the LinearLayout is replaced, but the button is not:
So you can set the visibility (as a commented in the code). But I suggest to create 2 fragments, one with the button and one with your content. Then in the activity onCreate() method, you can set the first fragment and replace it with the other one later.
((Button)findViewById(2131230768)).setOnClickListener(new MainMenuActivity(this));
View.OnClickListener runGameListener = new MainMenuActivity(this);
I have implemented onclicklistener even though it gives error on mainmenuactivity
my class is
public class MainMenuActivity
extends Activity implements OnClickListener
please help me..
Assume you have layout sample_activity.
Here's the code for sample_activity.xml :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="GO!!"
android:id="#+id/buttonGo"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="170dp" />
</RelativeLayout>
And MainActivity.class :
public class MainActivity extends ActionBarActivity {
Button goButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sample_activity);
goButton = (Button) findViewById(R.id.buttonGo);
goButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//....Do all the stuffs here
}
});
}
First, never search for views by their generated id value. Use the identifier name instead, e.g.
findViewById(R.id.your_button)
Second, new with an Activity is not correct. Don't instantiate activities yourself.
Since your activity implements View.OnClickListener and assuming the code is in the same activity, just use
setOnClickListener(this)
That is,
findViewById(R.id.your_button).setOnClickListener(this);
(setOnClickListener() is a View method so casting to Button is not needed here.)
Firstly check your id of button from xml is write or not? I think your id is wrong its giving error..
I defined a layout view in a .xml file called menu_list_slide_lateral.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/linear_menu_slide"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="#+id/btnSliBebe"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/bebe" />
<Button
android:id="#+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/foto_diario" />
<Button
android:id="#+id/button3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/consejos" />
<Button
android:id="#+id/button4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/ajustes" />
</LinearLayout>
Im creating the SlidingMenu from code:
setBehindContentView(R.layout.menu_list_slide_lateral);
setSlidingActionBarEnabled(true);
slideMenu = getSlidingMenu();
slideMenu.setMode(SlidingMenu.LEFT);
slideMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
slideMenu.setShadowWidthRes(R.dimen.shadow_width);
slideMenu.setBehindOffset(100);
slideMenu.setFadeDegree(0.35f);
And mi activity extends from SlidingFragmentActivity:
public class TimelineActivity extends SlidingFragmentActivity
It shows perfectly the menu, but i want to do some actions when the user chooses an option from the menu:
For example, i want to open another activity when i choose the "Bebe" option.
I tried to set a onClick event to that button but it doesn't seem to work, it makes nothing:
inflater = getLayoutInflater();
item = inflater.inflate(R.layout.menu_list_slide_lateral, null);
btnSliBebe = (Button) item.findViewById(R.id.btnSliBebe);
btnSliBebe.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View arg0) {
Log.e(TAG, "boton bebe");
}
});
How can i access that buttons and asign them a event?
Thanks!
What you should to is to create separate fragment for slide menu which will contain your layout R.layout.menu_list_slide_lateral and will handle actions. This is easy to do.
Now when you have this fragment you need to insert it into your slide activity.
setBehindContentView which you used is placeholder for menu. So create simple layout which will hold your menu fragment. E.g. call it R.layout.menu_frame and it should be like this:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/menu_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Then in your sliding activity set R.layout.menu_frame as behindContentView and add your menu fragment into this view.
public class BaseSlidingActivity extends SlidingFragmentActivity {
protected MenuFragment mSlidingMenuFragment;
private SlidingMenu mSlidingMenu;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set the Behind View
setBehindContentView(R.layout.menu_frame);
//if we create new menu - create new MenuFragment and insert it into
//menu_frame
if (savedInstanceState == null) {
FragmentTransaction t = this.getSupportFragmentManager().beginTransaction();
mSlidingMenuFragment = new MenuFragment();
t.replace(R.id.menu_frame, mSlidingMenuFragment);
t.commit();
}
//if activity was restored(e.g. on orientation change) find it in fragment
//manager
else {
mSlidingMenuFragment = (MenuFragment) this.getSupportFragmentManager().findFragmentById(R.id.menu_frame);
}
// customize the SlidingMenu
mSlidingMenu = getSlidingMenu();
mSlidingMenu.setShadowWidthRes(R.dimen.shadow_width);
mSlidingMenu.setShadowDrawable(R.drawable.shadow);
mSlidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset);
mSlidingMenu.setFadeDegree(0.35f);
mSlidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
}
I hope it helps. You can browse through source code of example at github slidinfmenu example
I have this app, that I created a custom dialog for. I must of goofed something up cause while the .show call on the dialog does indeed bring it up, it looks like a whole new fragment and it is not floating but instead replacing the ui with its contents. I did see in their help for DialogFragment:
http://hi-android.info/docs/reference/android/app/DialogFragment.html#Lifecycle
that one can embed a dialog as a regular fragment or not. Though I am not doing anything to do this so I cannot figure out why its acting like an embedded fragment and not floating. After thinking on it, is it the way I defined my XML definition? The dialogfragment example above didn't really give a definition for the xml layout, so maybe that is where my issue is? (Even added the gravity to the xml file, still no dice)
My xml definition for this Dialog is here:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textSize="20sp"
android:text = "Location:"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left"/>
<Spinner
android:id="#+id/location_spinner"
android:layout_width = "450sp"
android:layout_height="wrap_content"/>
<!-- fill out the data on the package total cost etc -->
</LinearLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button android:id="#+id/location_dlg_ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Okay"/>
<Button android:id="#+id/location_dlg_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cancel"/>
<Button android:id="#+id/location_dlg_new"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Create new..."/>
</LinearLayout>
</LinearLayout>
Like I said displays just fine, the code for the fragment:
package com.viciousbytes.studiotab.subactivities.dialogfragments;
import ... ...
public class LocationPicker extends DialogFragment {
ArrayList<Location> mLocations;
public static LocationPicker newInstance()
{
LocationPicker loc = new LocationPicker();
return loc;
}
private void setLocations(ArrayList<Location> loc)
{
mLocations=loc;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Pick a style based on the num.
int style = DialogFragment.STYLE_NORMAL, theme = android.R.style.Theme;
setStyle(style, theme);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.location_dialog, container, false);
Spinner spinner = (Spinner)v.findViewById(R.id.location_spinner);
ArrayAdapter<Location> adapter = new ArrayAdapter<Location>(v.getContext(), android.R.layout.simple_spinner_item, mLocations);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
if(mLocations==null)
spinner.setPrompt("No Locations");
else
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(new LocationSelectedListener());
// Watch for button clicks.
Button newBtn = (Button)v.findViewById(R.id.location_dlg_new);
newBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// When button is clicked, call up to owning activity.
//create new start that activity...
}
});
// Cancel do nothing dismissthis
Button cancelBtn = (Button)v.findViewById(R.id.location_dlg_cancel);
cancelBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// When button is clicked, call up to owning activity.
//create new start that activity...
}
});
// okay button means set listener with the selected location.
Button okBtn = (Button)v.findViewById(R.id.location_dlg_ok);
okBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// When button is clicked, call up to owning activity.
//create new start that activity...
}
});
return v;
}
}
It is called from a fragment itself? though does that matter? because I am calling a TimePIckerDialog and a DatePickerDialog and those work fine, but my calling code from my other fragment is:
void showLocationDialog() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
Fragment prev = getFragmentManager().findFragmentByTag("locpicker");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
// Create and show the dialog.
DialogFragment newFragment = LocationPicker.newInstance();
newFragment.show(ft, "locpicker");
}
Your constructors are wrong. Try to have just one static method newInstance to instantiate the fragment for all cases and use a Bundle to store the arguments that you want to use in the fragment. Refer to Basic Dialog section here and extend it to your case.