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);
}
}
}
}
Related
I am new to coding and I am stuck with something. I have a set of 5 slideshows with about 30 images for each. I have an activity with 5 buttons. I am using a ViewPagerAdapter to load the slides from my drawable folder. All is working for the first button using the code I am posting here. The code loads all ~30 images to make the slideshow. Now When I try to make a new ViewPagerAdapter (couldn't make a duplicate name so i renamed it ViewPagerAdapterBD. Not sure if this is wrong) for the second button to load different files from the drawable, the class in the code is grayed out saying "ViewPagerAdapterBD is never used". I continued to load the new images but the second button would still load the files from the first button. My question is, how can I have my 5 buttons open up different images from my drawable folder? What do I need to do to have my buttons open up different images to ultimately have each button display a different slideshow for each? Or better wording, I want to know how to go about using an imageslider on different activities using viewpageradapter?
BurstMode.java
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class BurstMode extends AppCompatActivity {
ViewPager viewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_burst_mode);
getSupportActionBar().setTitle("Burst Mode");
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
viewPager = (ViewPager) findViewById(R.id.viewPager);
ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(this);
viewPager.setAdapter(viewPagerAdapter);
}
}
activity_burst_mode.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".BurstMode">
<android.support.v4.view.ViewPager
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v4.view.ViewPager>
</RelativeLayout>
ViewPagerAdapter.java
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
public class ViewPagerAdapter extends PagerAdapter {
private Context context;
private LayoutInflater layoutInflater;
private Integer [] images = {R.drawable.bm1,R.drawable.bm2,R.drawable.bm3,
R.drawable.bm4,R.drawable.bm5,R.drawable.bm6,R.drawable.bm7,
R.drawable.bm8,R.drawable.bm8,R.drawable.bm10,R.drawable.bm11,
R.drawable.bm12,R.drawable.bm13,R.drawable.bm14,R.drawable.bm15,
R.drawable.bm16,R.drawable.bm17,R.drawable.bm18,R.drawable.bm19,
R.drawable.bm20,R.drawable.bm21,R.drawable.bm22,R.drawable.bm23,
R.drawable.bm24,R.drawable.bm26,R.drawable.bm27,R.drawable.bm28,
R.drawable.bm29,R.drawable.bm30,R.drawable.bm31};
public ViewPagerAdapter(Context context) {
this.context = context;
}
#Override
public int getCount() {
return images.length;
}
#Override
public boolean isViewFromObject(#NonNull View view, #NonNull Object o) {
return view == o;
}
#NonNull
#Override
public Object instantiateItem(#NonNull ViewGroup container, int position) {
layoutInflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = layoutInflater.inflate(R.layout.custom_layout_burst_mode,
null);
ImageView imageView = (ImageView) view.findViewById(R.id.imageView);
imageView.setImageResource(images[position]);
ViewPager vp = (ViewPager) container;
vp.addView(view,0);
return view;
}
#Override
public void destroyItem(#NonNull ViewGroup container, int position, #NonNull
Object object) {
ViewPager vp = (ViewPager) container;
View view = (View) object;
vp.removeView(view);
}
}
custom_layout_burst_mode.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal|center_vertical"
app:srcCompat="#android:drawable/btn_star_big_off"
tools:layout_editor_absoluteX="164dp"
tools:layout_editor_absoluteY="65dp" />
</LinearLayout>
Here is an image of the activity with the buttons: Image
I want to know how I can go about having each button open up different sets of images, different from the other buttons
Welcome to programming!
Go ahead and scrap the other class, ViewPagerAdapterBD. Instead, let's extend the other class you already wrote.
Consider the constructor for ViewPagerAdapter.
public ViewPagerAdapter(Context context) {
this.context = context;
}
Why not pass in an argument here which configures it to behave differently?
public ViewPagerAdapter(Context context, SlideShowType type) {
this.context = context
this.type = type
}
Go ahead and look up enumerated types in Java, it will help you create this SlideShowType enum. Once you've done that, your adapter can check this configuration in a switch statement, and make use of different arrays of images depending on the SlideShowType passed into the constructor!
Another option would be passing the array of image resources into the constructor directly. Let me know if you'd like me to follow up with more detail on anything.
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);
}
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".
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.
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.