Hide "refresh" action bar item while showing indetermidate progress bar in action bar [duplicate] - actionbarsherlock

How to show Indeterminate ProgressBar when Refresh button is pressed in ActionBarSherlock and again show Refresh Button when ViewGroup on refreshed?
Update 1:
I have a answer here which is incomplete. I am placing a bounty on question so that more developers can help build a good answer which can useful to others in future.
How can we show a Indeterminate ProgressBar which looks like the one shown in the image below

It seems like ActionBarSherlock doesn't provide specific method to animate a refresh MenuItem.
What you can do (by using classic android API) is to use the setActionView(int resId) method and give the id of a layout with a ProgressBar in it.
At the beginning of your refresh action just call :
item.setActionView(R.layout.refresh_menuitem);
And when your refresh action is finished call :
item.setActionView(null);
Here is a sample of what your layout file refresh_menuitem.xml can have :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:addStatesFromChildren="true"
android:focusable="true"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:gravity="center"
style="?attr/actionButtonStyle">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="true"
style="#android:style/Widget.ProgressBar.Small"/>
</LinearLayout>

Here is how you add this kind of indeterminate ProgressBar with a ActionBarSherlock object : (actually it's easier the other one but the progressBar is shown alone and not above a MenuItem)
1 - Put this line in the onCreate() method before the setContentView() call :
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-> This line specify that you will use the indeterminate ProgressBar function.
2 - Enable the indeterminate ProgressBar by calling :
setSupportProgressBarIndeterminateVisibility(true);
3 - Disable the indeterminate ProgressBar by calling :
setSupportProgressBarIndeterminateVisibility(false);
Remark : Have a look in the sample folder of the ActionBarSherlock folder. I found this code in the following file :
JakeWharton-ActionBarSherlock-9598f2b\samples\demos\src\com\actionbarsherlock\sample\demos\IndeterminateProgress.java

Here is a complete code:
private static final int menuItemIdRefresh = 10; //class constant
private boolean refresh;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuItem refreshItem = menu.add(0, menuItemIdRefresh, 0,
getString(R.string.action_refresh)).setShowAsActionFlags(
MenuItem.SHOW_AS_ACTION_ALWAYS);
if (isRefreshing) {
refreshItem.setActionView(R.layout.indeterminate_progress);
} else {
refreshItem.setActionView(null);
refreshItem.setIcon(R.drawable.ic_refresh);
}
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case menuItemIdRefresh: {
//the user has pressed the refresh button
if (!isRefreshing) {
isRefreshing = true;
new RefreshMyViewAsyncTask().execute("");
}
}
break;
default:
break;
}
return false;
}
One last note, in order to get the above code working you´ll need also call supportInvalidateOptionsMenu(). You can add that to the RefreshMyViewAsyncTask's onPreExecute() method.
Hope this helps to somebody.

Related

Single Activity with Navigation Component: how to handle different AppBar / Themes

I've been using the new Navigation Component since shortly after it has been announced at Google I/O, and also started to embrace the single-activity as much as possible.
The Single Activity allowed me to share ViewModels between view for an awesome experience and I really don't want to go back to multi-activity if I'm not forced to.
But there's something that gets in the way: AppBar / Themes (status bar) to the single activity concept.
This is part of the design I'm working in:
As you can see there are different requirments for how the Actionbar / status bar should look.
It's a simple drawer with standard actionbar
Classic detail with image going under the translucent status bar, supposed to use CollapsingToolbarLayout to turn into a standard actionbar when scrolling up
In this case it is non-standard actionbar, I'd call it a "floating toolbar" cause it doesn't expand to the full with of the screen and contains an already expanded SearchView / EditText
Fairly standard AppBar with tabs
List of issues that arise from leaving the single activity:
can't share ViewModels between activities
complex navigations which re-use parts already defined in another activity navigation graph have to be duplicated / moved into a dedicated activity
back navigation "re-construction" doesn't work between activities
Those are issues I want to avoid if possible, but how do you guys manage these kind of situation on a single-activity with navigation component. Any idea?
As mentioned here, the developer document said
Adding the top app bar to your activity works well when the app bar’s layout is similar for each destination in your app. If, however, your top app bar changes substantially across destinations, then consider removing the top app bar from your activity and defining it in each destination fragment, instead.
I was also thinking the same but never got time to do some experiment. So it's not a solution, it's an experiment, where I want to replace a view with another, here, the toolbar with a toolbar that contains an ImageView.
So I created a new Application using "Basic Activity" template. Then created two destinations within the graph, Home and destination. And lastly, created another layout for Toolbar:
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="?actionBarSize">
<ImageView
android:id="#+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="#mipmap/ic_launcher_round" />
</androidx.appcompat.widget.Toolbar>
The activity_main.xml has:
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
...
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
...
And then within Activity, of-course depends on the setup, but let's say that I want to setup an support-actionbar with toolbar:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
Toolbar toolbar2 = (Toolbar) getLayoutInflater().inflate(R.layout.destination_toolbar, null);
AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
navController = Navigation.findNavController(this, R.id.nav_host_fragment);
appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph())
.build();
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
switch (destination.getId()) {
case R.id.homeFragment:
appBarLayout.removeAllViews();
appBarLayout.addView(toolbar);
setSupportActionBar(toolbar);
toolbar.setTitle("Home Fragment");
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
break;
case R.id.destinationFragment:
appBarLayout.removeAllViews();
appBarLayout.addView(toolbar2);
setSupportActionBar(toolbar2);
toolbar2.setTitle("");
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
break;
}
});
}
And thus, this works, making it somewhat ugly as destination grows and new Toolbar/any other view is being added.
P.S. As I told earlier, this is just an experiment, if anyone has a better solution, please do post a new answer.
Disclaimer
Based of #Rajarshi original experiment, I made a working solution for this problem. I'm not sure is the most elegant, or if there are better ways. But after hours of research and investigation, this is the best solution I found.
Solution
Inflate the toolbars separately and store their references so they are not picked by the garbage collector.
Then load each on demand in your main AppBarLayout inside a custom OnDestinationChangedListener defined for your navController
Example
Here's an example I've written in Kotlin.
On your activity.xml layout, define an AppBarLayout that is empty.
layout/activity.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
...
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay" />
...
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Define the toolbars that your app needs to have in separate layout files.
layout/toolbar_defaul.xml
<com.google.android.material.appbar.MaterialToolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/default_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:menu="#menu/menu_default"
app:popupTheme="#style/AppTheme.PopupOverlay" />
layout/toolbar2.xml
<com.google.android.material.appbar.MaterialToolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar2"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:menu="#menu/menu2"
app:popupTheme="#style/AppTheme.PopupOverlay" />
In your main (and only) activity, declare AppBar related components as class properties, so that they are not picked up by the garbage collector.
Activity.kt
class Activity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var appBarLayout: AppBarLayout
private lateinit var defaultToolbar: MaterialToolbar
private lateinit var toolbar2: MaterialToolbar
...
And finally, in the onCreate method, define a OnDestinationChangedListener for the navController. Use it to load on demand each toolbar.
Activity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_ryvod)
// Set up AppBar
appBarLayout = findViewById(R.id.appbar)
appBarConfiguration = AppBarConfiguration(setOf(R.id.StartFragment))
defaultToolbar = layoutInflater.inflate(R.layout.toolbar_default, appBarLayout, false) as MaterialToolbar
toolbar2 = layoutInflater.inflate(R.layout.toolbar2, appBarLayout, false) as MaterialToolbar
val host =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment?
?: return
val navController = host.navController
navController.addOnDestinationChangedListener { _, destination, _ ->
when (destination.id) {
R.id.locationPickerFragment -> {
appBarLayout.removeAllViews()
appBarLayout.addView(toolbar2)
setSupportActionBar(toolbar2)
}
else -> {
appBarLayout.removeAllViews()
appBarLayout.addView(defaultToolbar)
setSupportActionBar(defaultToolbar)
}
}
setupActionBarWithNavController(navController, appBarConfiguration)
}
}
That should do the trick
I confronted this problem a while ago, with similar UX/UI as yours:
Sidenav Navigation Drawer
A "normal" Appbar with back arrow
Translucent Appbar/status bar
My solution was having a different .xml Appbar for each case and using the <include/> tag inside every fragment xml:
<include
android:id="#+id/include"
layout="#layout/default_toolbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
The window config for case 1 and case 2 was the same, but for the translucent Appbar, the window config changed, see case 3.
So I had to do a config change every time the fragment showed up/replaced:
public static void transparentStatusBar(Activity activity, boolean isTransparent, boolean fullscreen) {
if (isTransparent){
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}else {
if (fullscreen){
View decorView = activity.getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
} else {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
| View.SYSTEM_UI_FLAG_VISIBLE);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
}
And then use this method in translucent appbar/status bar fragment's lifecycle:
#Override
public void onResume() {
super.onResume();
UtilApp.transparentStatusBar(requireActivity(), true, true);
}
#Override
public void onStop() {
super.onStop();
UtilApp.transparentStatusBar(requireActivity(), false, false);
}

onTouchListener not working after layout change

I want to get feedback when the user swipes anywhere on the screen. I found some code that does that using an onTouchListener. And it worked.
Today I did some rework on my UI setup and added a lot of new layouts to organize it a little nicer. But my onTouch event is never triggering any more!
My guess is that some of the layouts might be overlapping and stealing the event. But I have not been successful trying to use the following arguments in the xml.
android:clickable="false"
android:focusable="false"
android:touchscreenBlocksFocus="false"
android:visibility="invisible"
This is the code running in onCreate:
View window = getWindow().getDecorView();
window.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("YEY! A TOUCH EVENT!");
return true;
}
});
RelativeLayout tab1 = (RelativeLayout) findViewById(R.id.layTab1);
tab1.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("YEY! A TOUCH EVENT!");
return true;
}
});
Any ideas as for what might have changed when adding new layouts is greatly appriciated!
You can try with Creating your layout design with frame layout
Like Below:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<View
android:id="#+id/view_main"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:scaleType="matrix"/>
//Your Layout design
</FrameLayout>
And make view_main .setOnTouchListner in code.. Other layout desing now can't steal touchlistner event from this way..
findViewById(R.id.view_main).setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("YEY! A TOUCH EVENT!");
return true;
}
});
I hope this can help you.let me know the result.
I finally figured out what the problem was after much troubleshooting.
My layout hierchy was the following:
- activity_main (RelativeLayout)
--- layTab1 (RelativeLayout)
----- (ScrollView)
------- layTab1View (LinearLayout)
--------- (STUFF FOR THIS TAB)
--- layTab2 (RelativeLayout)
----- (ScrollView)
------- layTab2View (LinearLayout)
--------- (STUFF FOR THIS TAB)
And I was trying to grab events from the activity_main layout. Which was working fine til I added a ScrollView. Apparently, the ScrollView consumes all the touch events. To work around this problem. I had to add a listener to all of the LinearLayout children of the ScrollView since only one tab will be active at the time.
I do how ever feel that having multiple different elements listen for the same event to trigger a menu popup is slightly unnessesary? So if at all possibly I would love to hear suggestions as for how I could solve this more "cleanly".

Android alert dialog button background

In Android Alert Dialog : dialog.getButton is not available
How to change the background of the Positive button in laert dialog
You need to write it after
dialog.show();
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(NewApplication.this);
alertDialogBuilder.setCancelable(false).setNegativeButton("Yes",new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
AlertDialog alertDialog = alertDialogBuilder.create();
alertDialog.show();
alertDialog.getButton(Dialog.BUTTON_NEGATIVE).
setBackgroundColor(Color.parseColor("#3399ff"));
I think this has been addressed here before, IRC.
Check these answers posted earlier on a similar issue:
Android Button modify Question.
Good links and answers there.
First off, the
'
`DialogInterface.OnClickListener`'
listener — must not be NULL .
Second make sure you imported the correct Android classes, there are a lot of them, and some are version specific.
For example:
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.os.Handler;
import android.util.Log;
Here is a quick and dirty sample of code in Java that illustrates the use of some special Android imports you need for an application. Remember Android uses many special classes and objects all its own.
final AlertDialog d = new AlertDialog.Builder(this)...create();
//final Button b1 = d.getButton(Dialog.BUTTON_POSITIVE);
editName.addTextChangedListener(new TextWatcher() //
{
public void afterTextChanged(Editable ed) {
Button b1 = d.getButton(Dialog.BUTTON_POSITIVE);
//comment out sections that may or may not work //
b1.setEnabled(ed.length() > 0);
private static final class [More ...] ButtonHandler extends Handler {
// Button clicks have Message.what as the BUTTON{1,2,3} constant//
private static final int MSG_DISMISS_DIALOG = 1;
private WeakReference<DialogInterface> mDialog;
public void // whatever classes you want //
ButtonHandler(DialogInterface dialog) {
mDialog = new WeakReference<DialogInterface>(dialog);
}
Use override to set replacement buttons or new button messages in a new section. It is one way to use the same button positioning for a new function. For example:
#Override
public void [More ...]
handleMessage(Message msg) {
switch (msg.what) {
case DialogInterface.BUTTON_POSITIVE:
case DialogInterface.BUTTON_NEGATIVE:
case DialogInterface.BUTTON_NEUTRAL:
((DialogInterface.OnClickListener)
msg.obj).onClick(mDialog.get(), msg.what);
break;
case MSG_DISMISS_DIALOG:
((DialogInterface) msg.obj).dismiss();
}
Hope this helps answer your question.
Code illustrated for illustration only, it is not the actual code, you need to write your own.
As an additional note: If you are using eclipse or other GUI interface, it tends to add a lot of additional and unnecessary code. Try coding on a text editor like Notepad++ and you will find it to be cleaner and smoother.
U try to Custom Alert dialog
<LinearLayout
android:id="#+id/ll_head"
android:layout_width="match_parent"
android:layout_height="30dp"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:background="#color/colorPrimaryDark"
app:layout_constraintRight_toRightOf="parent">
</LinearLayout>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="63dp"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_below="#+id/ll_head"
android:layout_alignParentStart="true"
android:background="#color/colorAccent"
android:layout_marginLeft="36dp"
android:layout_marginStart="36dp"
android:layout_marginBottom="36dp"
android:id="#+id/button" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/button"
android:layout_alignBottom="#+id/button"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:background="#android:color/holo_blue_bright"
android:layout_marginRight="53dp"
android:layout_marginEnd="53dp" /> </RelativeLayout>
public class MainActivity extends AppCompatActivity {
Dialog alertDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView=(TextView) findViewById(R.id.text);
textView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
alertDialog=new Dialog(MainActivity.this);
alertDialog.setContentView(R.layout.dialog);
alertDialog.show();
}
});
}
}

Spinner not working inside ViewGroup

From last few days Im trying to get Spinner working in ViewGroup without any success. :-{.
Inside custom ViewGroup I placed fragment container.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:boxMenu="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".TestActivity"
android:id="#+id/frame" >
<com.imper.boxmenulibrary.BoxMenuLayout
android:id="#+id/box_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
boxMenu:boxSize="40dp"
boxMenu:directionY="bottomToTop"
boxMenu:directionX="rightToLeft"
boxMenu:showStart="false"
android:padding="5dp"
android:background="#ffffff00">
<LinearLayout
android:id="#+id/fragment_container"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="top|center"
android:orientation="vertical">
</LinearLayout>
</com.imper.boxmenulibrary.BoxMenuLayout>
In that fragment container Im replacing fragment like this:
FragmentManager fm = ((Activity) context).getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fragment_container, new TestFragment());
ft.commit();
My TestFragment xml layout looks like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<Spinner
android:id="#+id/test_spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
And TestFragment implementation looks like this:
public class TestFragment extends BoxLayout implements OnItemSelectedListener {
Spinner testSpinner;
public TestFragment() {}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
View view = inflater.inflate(R.layout.test_view, container, false);
testSpinner = (Spinner) view.findViewById(R.id.test_spinner);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(container.getContext(), R.array.day, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
testSpinner.setAdapter(adapter);
testSpinner.setOnItemSelectedListener(this);
return view;
}
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
Toast.makeText(getActivity(), "Bingo", Toast.LENGTH_SHORT).show();
Log.d("Bingo", "onItemSelected");
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
Toast.makeText(getActivity(), "Bingo Nothing", Toast.LENGTH_SHORT).show();
Log.d("Bingo", "onNothingSelected");
}
}
Now, the problem is that when Im trying to select something from Spinner drop down, nothing is selected, OnItemSelected event is not fired at all.
The same code is working fine, when FragmentContainer is moved out from my custom ViewGroup.
Why that happening ??? How can I get Spinner working fine in custom view group ??
Second Edit --------------
I had a problem very similar to yours. (See the original post below for more detail.) Without knowing more about your custom ViewGroup implementation I don't know whether my solution will work for you or not.
My problem turned out to be that in my "layoutChildren" implementation I introduced an "optimization" whereby if I was reusing a child view whose content I knew hadn't changed (and the layout window hadn't changed either) I didn't bother to remeasure the child view.
It turns out if you're using Spinners you can't get away with this. The reason has to do with the convoluted way certain View.mPrivateFlags (PFLAGS_FORCE_LAYOUT and PFLAGS_LAYOUT_REQUIRED) are implemented relative to tracking the need to reprocess the layout of a child view IN COMBINATION WITH the way the Spinner decides to trigger the onItemSelected listener.
As noted below, Spinner fires the onItemSelected listener when it is asked to perform a layout and therein recognizes that the selected item has changed. If it doesn't perform a layout, it knows (internally) that the selected item has changed, but doesn't call the onItemSelected listener.
From the Spinner's perspective this is ok, because when the Spinner saw the new item selected, it specifically requested a layout pass (requestLayout).
The layout pass begins and propagates down to my custom ViewGroup. When it gets to the first child that child has its PFLAGS_FORCE_LAYOUT bit set...but that flag is ignored by the processing logic. Instead it is looking only for the PFLAGS_LAYOUT_REQUIRED bit, and when it doesn't see that flag it decides it is not necessary to propagate the layout any further down the view hierarchy, and so the Spinner never gets asked to perform a layout and never fires the onItemSelected listener.
The fix was to remove the optimization to not measure child views whose dimensions are already known. PFLAGS_LAYOUT_REQUIRED (which is package private...you can't get to it from anything you write) only gets set when the child views are asked to measure themselves, and even though the measurement itself was not strictly necessary in my case, it is the only way to get the mPrivateFlags manipulated into the correct state.
Check to be sure you are measuring your child views (particularly views that are being re-used from prior layout passes) and see if this addresses your problem.
First Edit ---------------
I've made some progress on this, but still haven't solved it. The Spinner calls the onItemSelected listener when its onLayout() method is called and it recognizes that the selected item has changed.
When the Spinner is hosted by a ListView, the ListView.onLayout(...) gets called after the selection in the Spinner is changed (though I haven't been able to figure out how), and the ListView onLayout method propagates that call down to the Spinner, resulting in an onItemSelected() call.
When the Spinner is hosted by my custom AdapterView, the onLayout method never gets called after the Spinner is changed, and so no onItemSelected() call is made.
I just haven't yet been able to figure out why, where, or how, a layout request is being triggered in the one case but not in the other. Because the layout request is posted to the message queue and executed from there, it is very difficult to determine where it originated.
Let me know if this helps you any, or gives you any ideas.
Original Post ------------
I'm having the same problem. I created a custom ViewGroup (extended AdapterView) and now child views that include spinners don't fire the onItemSelected listener. If I attach the same child view to a ListView (also extended from AdapterView) it works fine. I did something wrong in my custom ViewGroup, but I know not what.
Did you find a solution to your problem? If so, can you post it?
This post is very old but it helped me tremendously - though I STILL had the problem (though every OTHER time I selected something from the dropdown - rather than every time).
If it helps - the fix for my issue was to NOT call bringToFront on the spinner object that was displaying the problem.
As Steve has pointed out already, the problem is that certain flags need to be manipulated to actually force a layout, and the only (public) way to do so is to call measure()...
As a workaround, adding the following method to my custom ViewGroup addressed the problem for me, but you might want to make sure not to make too many extra/redundant measurements, depending on the actual use case...
public void requestLayout() {
super.requestLayout();
measure(MeasureSpec.EXACTLY | getMeasuredWidth(),
MeasureSpec.EXACTLY | getMeasuredHeight());
}
Minimal code snippet reproducing the problem:
package org.kobjects.spinnertestapp;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import java.util.ArrayList;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ArrayList<String> options = new ArrayList<>();
options.add("Hello");
options.add("Wutan");
options.add("Lorem Ipsum");
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(this,android.R.layout.simple_spinner_dropdown_item, options);
Spinner spinner = new Spinner(this);
spinner.setAdapter(arrayAdapter);
MyLayout layout = new MyLayout(this);
layout.addView(spinner);
setContentView(layout);
}
static class MyLayout extends ViewGroup {
public MyLayout(Context context) {
super(context);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
for (int i = 0; i < getChildCount(); i ++) {
getChildAt(i).layout(left, top, right, bottom);
}
}
}
}

Android Fragment Interface Unresponsive

I am having a strange issue with my program that I cannot explain and I have thus far not been able to find a solution. I have a simple activity that will switch between fragments and run the user through an initial setup of the app. The first fragment is just a text view at the top, with a button on the bottom with an onClickListener set to call a method on the parent activity, however in testing, when I click on the button, nothing happens. It does not change color like a normal button would, and no click seems to be registered.
Here is the XML for the layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:text="#string/setup_intro" />
<Button
android:id="#+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:clickable="true"
android:width="72dp"
android:text="#string/next_button" />
</RelativeLayout>
And this is the fragment code where I implement the onClickListener
public class SetupFragmentInitialScreen extends SherlockFragment
{
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState)
{
View parentView = null;
parentView = inflater.inflate(R.layout.setup_fragment_initial_screen,
container,
false);
Button nextButton = (Button)parentView.findViewById(R.id.next_button);
nextButton.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
Log.v("ButtonPressed", "You Pressed the button!");
((InitialActivity)getActivity()).onInitialScreenNextPress();
}
});
return parentView;
}
}
And lastly, here is my code for my activity so far
public class InitialActivity extends SherlockFragmentActivity
{
private SetupFragmentInitialScreen initialScreen;
private SetupFragmentPreferenceOneScreen preferenceOneScreen;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
initialScreen = new SetupFragmentInitialScreen();
preferenceOneScreen = new SetupFragmentPreferenceOneScreen();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(android.R.anim.fade_in,
android.R.anim.fade_out);
fragmentTransaction.replace(android.R.id.content,
initialScreen);
fragmentTransaction.commit();
}
public void onInitialScreenNextPress()
{
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(android.R.anim.fade_in,
android.R.anim.fade_out);
fragmentTransaction.replace(android.R.id.content,
preferenceOneScreen);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
}
So far the code to me seems correct, but as I said, there is no reaction from the interface when I try to press the button.
Edit: I have added the following code to my Activity to check for touch events
#Override
public boolean onTouchEvent(MotionEvent event)
{
super.onTouchEvent(event);
Log.v("Touch Detected", "You are touching the screen.");
return false;
}
It logs events all over the screen, except for when I'm touching the button, so the activity is receiving touch events, but the UI itself is not. I also tried loading another interface which has a pair of radio buttons, and they too were unresponsive. Is there something I'm doing wrong with initializing the fragments?
Unfortunately none of the code you posted seems to point to what the issue is.
A button does not need an onClick listener in order to change color when pressed, so I wouldn't worry about that part. More importantly:
Is it possible that any transparent view is lying on top of the button and taking the click? DDMS has a "Dump View Hierarchy for UI Automator" button that may help you check on this.
Does your activity override dispatchTouchEvent(), onInterceptTouchEvent(), or related API's and could one of these be preventing the touch from reaching the button?
If you are applying custom theming to the button, do you have separate visuals for the pressed state?
I figured out what it was. For whatever reason the program didn't like me trying to do the fade out-fade in animation when loading the first fragment into the activity. I removed that line from the onCreate() method and it works fine now.

Resources