I have a map in a gallery, together with other pages.
When I am sliding the gallery pages with touch gestures everything works fine.
But when I move the map away using a button on a dialog, and then slide back to the map, the map does no longer smoothely follow the panning gesture. It rather follows with a noticeable delay.
As soon as I hide the map once again with a dialog and close the dialog, e.g. with the back button, the behaviour returns to normal.
To reproduce it, I have stripped down the code as much as possible.
The gallery contains a DummyView, then a MapView, and finally again a DummyView as pages. DummyView and GalleryView are added as local classes to the Activity.
public class MainActivity extends FragmentActivity {
private static final int REQUEST_CODE_RECOVER_PLAY_SERVICES = 0;
private Gallery gallery;
private MapView mapView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map_in_gallery);
final View[] views = new View[3];
views[0] = new DummyView(this, "Page 1");
views[1] = mapView = new MapView(this);
views[2] = new DummyView(this, "Page 3");
gallery = (Gallery) findViewById(R.id.gallery);
BaseAdapter adapter = new ArrayAdapter<View>(this, 0, views) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
return views[position];
}
};
gallery.setAdapter(adapter);
// Let every page cover the whole screen
gallery.setUnselectedAlpha(1);
gallery.setSpacing(30);
}
#Override
protected void onResume() {
super.onResume();
checkPlayServices();
};
#Override
/** Define the dialog, which appears, when the Page-Button is pressed and
* which allows to page in the gallery instead of swiping.
*/
protected Dialog onCreateDialog(int id) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Page");
builder.setPositiveButton("->", new OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
page(+1);
}
});
builder.setNegativeButton("<-", new OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
page(-1);
}
});
return builder.create();
}
private void page(int increment) {
int count = gallery.getAdapter().getCount();
int nextPos = gallery.getSelectedItemPosition() + increment;
if (nextPos >= count) {
nextPos = 0;
} else if (nextPos < 0) {
nextPos = count - 1;
}
gallery.setSelection(nextPos);
}
private boolean checkPlayServices() {
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (status != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(status)) {
showErrorDialog(status);
} else {
Toast.makeText(this, "This device is not supported.",
Toast.LENGTH_LONG).show();
finish();
}
return false;
}
return true;
}
void showErrorDialog(int code) {
GooglePlayServicesUtil.getErrorDialog(code, this,
REQUEST_CODE_RECOVER_PLAY_SERVICES).show();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CODE_RECOVER_PLAY_SERVICES:
if (resultCode == RESULT_CANCELED) {
Toast.makeText(this, "Google Play Services must be installed.",
Toast.LENGTH_SHORT).show();
finish();
}
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
public void onButtonClick1(View v) {
showDialog(0); // Only one dialog defined
}
public void onButtonClick2(View v) {
mapView.togglePanOrSlide();
}
}
class DummyView extends View {
private String text;
private Paint paint;
/** Simple view which draws a text in the center of the screen. */
public DummyView(Context context, String text) {
super(context);
this.text = text;
paint = new Paint();
paint.setColor(Color.WHITE);
paint.setTextSize(20);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText(text, getWidth() / 2, getHeight() / 2, paint);
}
}
class MapView extends RelativeLayout {
/** Switches the map gestures off, to allow sliding the map in the gallery. */
private boolean panAllowed;
public MapView(Context context) {
super(context);
inflate(context, R.layout.map_fragment, this);
}
public void togglePanOrSlide() {
panAllowed = !panAllowed;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (!panAllowed) {
// Catch the event from the map, so the gallery will slide the page
// instead.
return true;
}
return super.onInterceptTouchEvent(event);
}
}
Here are the two layouts inflated in the coding:
activity_map_in_gallery.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="#+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onButtonClick1"
android:text="Open Paging Dialog" />
<Button
android:id="#+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onButtonClick2"
android:text="Pan Map <-> Slide Page" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1" >
<Gallery
android:id="#+id/gallery"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:background="#android:drawable/alert_dark_frame" />
</RelativeLayout>
</LinearLayout>
and map_fragment.xml:
<?xml version="1.0" encoding="utf-8"?>
<!-- Merged into a RelativeLayout-Extension -->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment" />
</merge>
The layout adds two buttons on top of the gallery. The first opens a dialog, which allows to page back and forth in the gallery.
The second button switches the maps behaviour. It can be either panned, or its full page can be slid to the left or right.
When the map is paged away using the first button, and then slid back using a slide gesture the problem appears.
The second button allows to switch off the maps panning, so the map reacts on usual gallery sliding. When the map is paged back and forth using sliding gestures, the panning works afterwards without problem (after the second button was pressed again.)
Any idea, why this happens, and how to avoid it?
The fragment transaction is performed before the dialog is closed, this causes the weird behavior.
Instead of directly performing the fragment transaction, post it on the Handler. Once the dialog is closed, then the fragment transaction will be performed.
Try following code. I've applied same logic as done in one of my SO answer.
private void page(int increment) {
int count = gallery.getAdapter().getCount();
int nextPos = gallery.getSelectedItemPosition() + increment;
if (nextPos >= count) {
nextPos = 0;
} else if (nextPos < 0) {
nextPos = count - 1;
}
final int finalNextPos = nextPos;
Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
gallery.setSelection(finalNextPos);
}
});
}
Related
I've seen a lot of discussions on boommenu. But I didn't see anywhere about functionality on event handling on each click on these following menu item in which displays after clicking on BoomMenuButton.
I've been reading all the solutions but none of them could help me. Here is my code
activity_mail.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/activity_simple_circle_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.nightonke.boommenu.BoomMenuButton
android:id="#+id/bmb3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:bmb_normalColor="#android:color/holo_blue_dark"
app:bmb_highlightedColor="#android:color/holo_blue_bright"
app:bmb_buttonEnum="textOutsideCircle"
app:bmb_piecePlaceEnum="piecePlace_share"
app:bmb_buttonPlaceEnum="buttonPlace_custom">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="#drawable/middle_big_button_trans"
android:layout_gravity="center" />
</com.nightonke.boommenu.BoomMenuButton>
</LinearLayout>
And Activity class is
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";
private BoomMenuButton boomMenuButton;
public MainActivity() {
Log.d(TAG,"Inside MainActivity's default Constructor");
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"Setting the layout before it's in inflates");
setContentView(R.layout.activity_main);
Log.d(TAG,"Before initializing Bmb3");
initializeBmb3();
Log.d(TAG,"After initializing Bmb3");
}
private void initializeBmb3() {
Log.d(TAG,"Inside initializeBmb3()");
BoomMenuButton bmb = (BoomMenuButton) findViewById(R.id.bmb3);
for (int i = 0; i < 3; i++) {
Log.d(TAG,"Inside for loop -> Before adding builder");
bmb.addBuilder(BuilderManager.getTextOutsideCircleButtonBuilderWithDifferentPieceColor());
Log.d(TAG,"Inside for loop -> After adding builder");
}
float w = Util.dp2px(80);
float h = Util.dp2px(96);
float h_0_5 = h / 2;
float h_1_5 = h * 1.5f;
float hm = bmb.getButtonHorizontalMargin();
float vm = bmb.getButtonVerticalMargin();
float vm_0_5 = vm / 2;
float vm_1_5 = vm * 1.5f;
bmb.getCustomButtonPlacePositions().add(new PointF(-w - hm, +h_0_5 + vm_0_5));
bmb.getCustomButtonPlacePositions().add(new PointF(+w + hm, +h_0_5 + vm_0_5));
bmb.getCustomButtonPlacePositions().add(new PointF( 0, +h_1_5 + vm_1_5));
for (int i = 0; i < bmb.getPiecePlaceEnum().pieceNumber(); i++) {
bmb.getBuilder(i)
.listener(new OnBMClickListener() {
#Override
public void onBoomButtonClick(int index) {
// When the boom-button corresponding this builder is clicked.
Toast.makeText(MainActivity.this, "Clicked " + index, Toast.LENGTH_SHORT).show();
}
});
}
Log.d(TAG,"At the end of initializeBmb3");
}
}
How could I add click functionality on each item displaying in boom?
Please take a look at the documentation here
Excerpt from the documentation:
Listener for BMB
If you want to manager all the click events in a method, you can use
OnBoomListener / OnBoomListenerAdapter:
// Use OnBoomListener to listen all methods
bmb.setOnBoomListener(new OnBoomListener() {
#Override
public void onClicked(int index, BoomButton boomButton) {
// If you have implement listeners for boom-buttons in builders,
// then you shouldn't add any listener here for duplicate callbacks.
}
#Override
public void onBackgroundClick() {
textViewForAnimation.setText("Click background!!!");
}
#Override
public void onBoomWillHide() {
textViewForAnimation.setText("Will RE-BOOM!!!");
}
#Override
public void onBoomDidHide() {
textViewForAnimation.setText("Did RE-BOOM!!!");
}
#Override
public void onBoomWillShow() {
textViewForAnimation.setText("Will BOOM!!!");
}
#Override
public void onBoomDidShow() {
textViewForAnimation.setText("Did BOOM!!!");
}
});
What I've done when building up the menus in the for loop is this:
for (int i = 0; i < HamBmb.getPiecePlaceEnum().pieceNumber(); i++) {
HamButton.Builder builder = new HamButton.Builder().rotateImage(true);
switch (i) {
case 0:
builder.normalImageRes(R.drawable.ic_launcher_foreground)
.normalText("Menu 1")
.listener(new OnBMClickListener() {
#Override
public void onBoomButtonClick(int index) {
HandleBoomMenuClick(index);
}
});
break;
case 1:
builder.normalImageRes(R.drawable.ic_launcher_foreground)
.normalText("Menu 2")
.listener(new OnBMClickListener() {
#Override
public void onBoomButtonClick(int index) {
HandleBoomMenuClick(index);
}
});
break;
case 2:
builder.normalImageRes(R.drawable.ic_launcher_foreground)
.normalText("Menu 3")
.listener(new OnBMClickListener() {
#Override
public void onBoomButtonClick(int index) {
HandleBoomMenuClick(index);
}
});
break;
default:
}
HamBmb.addBuilder(builder);
}
I have another method that, in my case, opens another activity:
private void HandleBoomMenuClick(int index) {
if (index < HamBmb.getPiecePlaceEnum().pieceNumber()) {
Intent intent;
switch (index) {
case 0:
intent = new Intent(this, YOURACTIVITY0.class);
startActivity(intent);
break;
case 1:
intent = new Intent(this, YOURACTIVITY1.class);
startActivity(intent);
break;
case 2:
intent = new Intent(this, YOURACTIVITY2.class);
startActivity(intent);
break;
default:
}
}
}
Sorry for coming to the game so late...
WhatsApp has such Toolbar:
When 'Search' menu item clicked, from the top SearchView comes down which takes whole space of toolbar:
When I tried to implement SearchView, it looks like this:
I found some libraries to implement this:
Android Material SearchView by Eugene Horan
and MaterialSearchView by krishnakapil. But they are not like in WhatsApp.
This question may seem weird, I could not find the way how to do this. So my question is how to implement WhatsApp like material design SearchView which comes from the top?
I have developed a well received library by the comunity.
Does exactly what are you looking for.
Give it a try and tell if if was usufull for you.
Here it is the Github repo for MaterialSearchView.
You can create this with android.support.v7 library
First of all create menu item in menu.xml like:
<item android:id="#+id/action_search"
android:title="Search"
android:icon="#drawable/abc_ic_search_api_mtrl_alpha"
app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView" />
Extend AppCompatActivity and retrieve the SearchView in onCreateOptionsMenu like:
import android.support.v7.widget.SearchView;
...
public class YourActivity extends AppCompatActivity {
...
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_home, menu);
// Retrieve the SearchView and plug it into SearchManager
final SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.action_search));
SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE);
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
return true;
}
...
}
Thats it. Hope this helps you. Accept, if you find it useful.
Sorry, I misread the question.
This answer should solve your issue.
Answer taken from :- Creating a SearchView that looks like the material design guidelines
After a week of puzzling over this. I think I've figured it out.
I'm now using just an EditText inside of the Toolbar. This was suggested to me by oj88 on reddit.
I now have this:
First inside onCreate() of my activity I added the EditText with an image view on the right hand side to the Toolbar like this:
// Setup search container view
searchContainer = new LinearLayout(this);
Toolbar.LayoutParams containerParams = new Toolbar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
containerParams.gravity = Gravity.CENTER_VERTICAL;
searchContainer.setLayoutParams(containerParams);
// Setup search view
toolbarSearchView = new EditText(this);
// Set width / height / gravity
int[] textSizeAttr = new int[]{android.R.attr.actionBarSize};
int indexOfAttrTextSize = 0;
TypedArray a = obtainStyledAttributes(new TypedValue().data, textSizeAttr);
int actionBarHeight = a.getDimensionPixelSize(indexOfAttrTextSize, -1);
a.recycle();
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, actionBarHeight);
params.gravity = Gravity.CENTER_VERTICAL;
params.weight = 1;
toolbarSearchView.setLayoutParams(params);
// Setup display
toolbarSearchView.setBackgroundColor(Color.TRANSPARENT);
toolbarSearchView.setPadding(2, 0, 0, 0);
toolbarSearchView.setTextColor(Color.WHITE);
toolbarSearchView.setGravity(Gravity.CENTER_VERTICAL);
toolbarSearchView.setSingleLine(true);
toolbarSearchView.setImeActionLabel("Search", EditorInfo.IME_ACTION_UNSPECIFIED);
toolbarSearchView.setHint("Search");
toolbarSearchView.setHintTextColor(Color.parseColor("#b3ffffff"));
try {
// Set cursor colour to white
// https://stackoverflow.com/a/26544231/1692770
// https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/widget/TextView.java#L562-564
Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
f.setAccessible(true);
f.set(toolbarSearchView, R.drawable.edittext_whitecursor);
} catch (Exception ignored) {
}
// Search text changed listener
toolbarSearchView.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Fragment mainFragment = getFragmentManager().findFragmentById(R.id.container);
if (mainFragment != null && mainFragment instanceof MainListFragment) {
((MainListFragment) mainFragment).search(s.toString());
}
}
#Override
public void afterTextChanged(Editable s) {
// https://stackoverflow.com/a/6438918/1692770
if (s.toString().length() <= 0) {
toolbarSearchView.setHintTextColor(Color.parseColor("#b3ffffff"));
}
}
});
((LinearLayout) searchContainer).addView(toolbarSearchView);
// Setup the clear button
searchClearButton = new ImageView(this);
Resources r = getResources();
int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, r.getDisplayMetrics());
LinearLayout.LayoutParams clearParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
clearParams.gravity = Gravity.CENTER;
searchClearButton.setLayoutParams(clearParams);
searchClearButton.setImageResource(R.drawable.ic_close_white_24dp); // TODO: Get this image from here: https://github.com/google/material-design-icons
searchClearButton.setPadding(px, 0, px, 0);
searchClearButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
toolbarSearchView.setText("");
}
});
((LinearLayout) searchContainer).addView(searchClearButton);
// Add search view to toolbar and hide it
searchContainer.setVisibility(View.GONE);
toolbar.addView(searchContainer);
This worked, but then I came across an issue where onOptionsItemSelected() wasn't being called when I tapped on the home button. So I wasn't able to cancel the search by pressing the home button. I tried a few different ways of registering the click listener on the home button but they didn't work. Eventually I found out that the ActionBarDrawerToggle I had was interfering with things, so I removed it. This listener then started working:
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// toolbarHomeButtonAnimating is a boolean that is initialized as false. It's used to stop the user pressing the home button while it is animating and breaking things.
if (!toolbarHomeButtonAnimating) {
// Here you'll want to check if you have a search query set, if you don't then hide the search box.
// My main fragment handles this stuff, so I call its methods.
FragmentManager fragmentManager = getFragmentManager();
final Fragment fragment = fragmentManager.findFragmentById(R.id.container);
if (fragment != null && fragment instanceof MainListFragment) {
if (((MainListFragment) fragment).hasSearchQuery() || searchContainer.getVisibility() == View.VISIBLE) {
displaySearchView(false);
return;
}
}
}
if (mDrawerLayout.isDrawerOpen(findViewById(R.id.navigation_drawer)))
mDrawerLayout.closeDrawer(findViewById(R.id.navigation_drawer));
else
mDrawerLayout.openDrawer(findViewById(R.id.navigation_drawer));
}
});
So I can now cancel the search with the home button, but I can't press the back button to cancel it yet. So I added this to onBackPressed():
FragmentManager fragmentManager = getFragmentManager();
final Fragment mainFragment = fragmentManager.findFragmentById(R.id.container);
if (mainFragment != null && mainFragment instanceof MainListFragment) {
if (((MainListFragment) mainFragment).hasSearchQuery() || searchContainer.getVisibility() == View.VISIBLE) {
displaySearchView(false);
return;
}
}
I created this method to toggle visibility of the EditText and menu item:
public void displaySearchView(boolean visible) {
if (visible) {
// Stops user from being able to open drawer while searching
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
// Hide search button, display EditText
menu.findItem(R.id.action_search).setVisible(false);
searchContainer.setVisibility(View.VISIBLE);
// Animate the home icon to the back arrow
toggleActionBarIcon(ActionDrawableState.ARROW, mDrawerToggle, true);
// Shift focus to the search EditText
toolbarSearchView.requestFocus();
// Pop up the soft keyboard
new Handler().postDelayed(new Runnable() {
public void run() {
toolbarSearchView.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 0, 0, 0));
toolbarSearchView.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, 0, 0, 0));
}
}, 200);
} else {
// Allows user to open drawer again
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
// Hide the EditText and put the search button back on the Toolbar.
// This sometimes fails when it isn't postDelayed(), don't know why.
toolbarSearchView.postDelayed(new Runnable() {
#Override
public void run() {
toolbarSearchView.setText("");
searchContainer.setVisibility(View.GONE);
menu.findItem(R.id.action_search).setVisible(true);
}
}, 200);
// Turn the home button back into a drawer icon
toggleActionBarIcon(ActionDrawableState.BURGER, mDrawerToggle, true);
// Hide the keyboard because the search box has been hidden
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(toolbarSearchView.getWindowToken(), 0);
}
}
I needed a way to toggle the home button on the toolbar between the drawer icon and the back button. I eventually found the method below in this SO answer. Though I modified it slightly to made more sense to me:
private enum ActionDrawableState {
BURGER, ARROW
}
/**
* Modified version of this, https://stackoverflow.com/a/26836272/1692770<br>
* I flipped the start offset around for the animations because it seemed like it was the wrong way around to me.<br>
* I also added a listener to the animation so I can find out when the home button has finished rotating.
*/
private void toggleActionBarIcon(final ActionDrawableState state, final ActionBarDrawerToggle toggle, boolean animate) {
if (animate) {
float start = state == ActionDrawableState.BURGER ? 1.0f : 0f;
float end = Math.abs(start - 1);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ValueAnimator offsetAnimator = ValueAnimator.ofFloat(start, end);
offsetAnimator.setDuration(300);
offsetAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
offsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float offset = (Float) animation.getAnimatedValue();
toggle.onDrawerSlide(null, offset);
}
});
offsetAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
toolbarHomeButtonAnimating = false;
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
toolbarHomeButtonAnimating = true;
offsetAnimator.start();
}
} else {
if (state == ActionDrawableState.BURGER) {
toggle.onDrawerClosed(null);
} else {
toggle.onDrawerOpened(null);
}
}
}
This works, I've managed to work out a few bugs that I found along the way. I don't think it's 100% but it works well enough for me.
EDIT: If you want to add the search view in XML instead of Java do this:
toolbar.xml:
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar"
contentInsetLeft="72dp"
contentInsetStart="72dp"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="4dp"
android:minHeight="?attr/actionBarSize"
app:contentInsetLeft="72dp"
app:contentInsetStart="72dp"
app:popupTheme="#style/ActionBarPopupThemeOverlay"
app:theme="#style/ActionBarThemeOverlay">
<LinearLayout
android:id="#+id/search_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal">
<EditText
android:id="#+id/search_view"
android:layout_width="0dp"
android:layout_height="?attr/actionBarSize"
android:layout_weight="1"
android:background="#android:color/transparent"
android:gravity="center_vertical"
android:hint="Search"
android:imeOptions="actionSearch"
android:inputType="text"
android:maxLines="1"
android:paddingLeft="2dp"
android:singleLine="true"
android:textColor="#ffffff"
android:textColorHint="#b3ffffff" />
<ImageView
android:id="#+id/search_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:src="#drawable/ic_close_white_24dp" />
</LinearLayout>
</android.support.v7.widget.Toolbar>
onCreate() of your Activity:
searchContainer = findViewById(R.id.search_container);
toolbarSearchView = (EditText) findViewById(R.id.search_view);
searchClearButton = (ImageView) findViewById(R.id.search_clear);
// Setup search container view
try {
// Set cursor colour to white
// https://stackoverflow.com/a/26544231/1692770
// https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/widget/TextView.java#L562-564
Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
f.setAccessible(true);
f.set(toolbarSearchView, R.drawable.edittext_whitecursor);
} catch (Exception ignored) {
}
// Search text changed listener
toolbarSearchView.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Fragment mainFragment = getFragmentManager().findFragmentById(R.id.container);
if (mainFragment != null && mainFragment instanceof MainListFragment) {
((MainListFragment) mainFragment).search(s.toString());
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
// Clear search text when clear button is tapped
searchClearButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
toolbarSearchView.setText("");
}
});
// Hide the search view
searchContainer.setVisibility(View.GONE);
I've just post my open source which imitates exactly what whatsapp toolbar does (Include circular animation).
Code
Full example
I'm new to working with floating action button and trying to get a few of the basic things working today. Currently I am stuck on getting the onClick functionality to work. I pulled most of the code from googles FAB basic example, and in there it has an onChecked method which sends a string to a logger to show you have clicked it.
#Override
public void onCheckedChanged(FloatingActionButton fabView, boolean isChecked) {
// When a FAB is toggled, log the action.
switch (fabView.getId()){
case R.id.fab_1:
break;
default:
break;
}
}
I was thinking I'd be able to replace the functionality in there but that had no affect. So I tried to create the onClickListener like you would with any other button but that also had no affect. I am not sure how to continue since neither option worked. my goal is just to produce a dialog when the floating action button is clicked, but for now I am just trying to use a placeholder alert dialog.
This is the FloatingActionButtonFragment class:
public class FloatingActionButtonFragment extends Fragment implements FloatingActionButton.OnCheckedChangeListener {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fab_layout, container, false);
// Make this {#link Fragment} listen for changes in both FABs.
FloatingActionButton fab1 = (FloatingActionButton) rootView.findViewById(R.id.fab_1);
fab1.setOnCheckedChangeListener(this);
fab1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("Are you sure?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog
}
});
// Create the AlertDialog object and return it
AlertDialog dialog = builder.create();
dialog.show();
}
});
return rootView;
}
#Override
public void onCheckedChanged(FloatingActionButton fabView, boolean isChecked) {
// When a FAB is toggled, log the action.
switch (fabView.getId()){
case R.id.fab_1:
break;
default:
break;
}
}
}
And here is the FloatingActionButton class:
public class FloatingActionButton extends FrameLayout implements Checkable {
/**
* Interface definition for a callback to be invoked when the checked state
* of a compound button changes.
*/
public static interface OnCheckedChangeListener {
/**
* Called when the checked state of a FAB has changed.
*
* #param fabView The FAB view whose state has changed.
* #param isChecked The new checked state of buttonView.
*/
void onCheckedChanged(FloatingActionButton fabView, boolean isChecked);
}
/**
* An array of states.
*/
private static final int[] CHECKED_STATE_SET = {
android.R.attr.state_checked
};
private static final String TAG = "FloatingActionButton";
// A boolean that tells if the FAB is checked or not.
private boolean mChecked;
// A listener to communicate that the FAB has changed it's state
private OnCheckedChangeListener mOnCheckedChangeListener;
public FloatingActionButton(Context context) {
this(context, null, 0, 0);
}
public FloatingActionButton(Context context, AttributeSet attrs) {
this(context, attrs, 0, 0);
}
public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr);
setClickable(true);
// Set the outline provider for this view. The provider is given the outline which it can
// then modify as needed. In this case we set the outline to be an oval fitting the height
// and width.
setOutlineProvider(new ViewOutlineProvider() {
#Override
public void getOutline(View view, Outline outline) {
outline.setOval(0, 0, getWidth(), getHeight());
}
});
// Finally, enable clipping to the outline, using the provider we set above
setClipToOutline(true);
}
/**
* Sets the checked/unchecked state of the FAB.
* #param checked
*/
public void setChecked(boolean checked) {
// If trying to set the current state, ignore.
if (checked == mChecked) {
return;
}
mChecked = checked;
// Now refresh the drawable state (so the icon changes)
refreshDrawableState();
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, checked);
}
}
/**
* Register a callback to be invoked when the checked state of this button
* changes.
*
* #param listener the callback to call on checked state change
*/
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
mOnCheckedChangeListener = listener;
}
#Override
public boolean isChecked() {
return mChecked;
}
#Override
public void toggle() {
setChecked(!mChecked);
}
/**
* Override performClick() so that we can toggle the checked state when the view is clicked
*/
#Override
public boolean performClick() {
toggle();
return super.performClick();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// As we have changed size, we should invalidate the outline so that is the the
// correct size
invalidateOutline();
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
}
There isn't much to either class at this point, they are mostly husks, but I just want to get this basic functionality down before I continue, and being the noob I am, I don't know why this wouldn't work.
If you are not already heading for deadline, you must change the floating action button to the one provided by google in design library
just follow http://android-developers.blogspot.in/2015/05/android-design-support-library.html
Add to the XML Layout:
<android.support.design.widget.FloatingActionButton
android:id="#+id/myFAB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/your_icon"
app:elevation="4dp"
... />
Add to the code behind:
FloatingActionButton myFab = (FloatingActionButton) myView.findViewById(R.id.myFAB);
myFab.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
doMyThing();
}
});
For more details follow : FloatingActionButton example with Support Library
Actually now with android support library it was very easy to add FAB and to customize it with click listeners
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// FAB Action goes here
}
});
Reference : http://androidgifts.com/android-material-design-floating-action-button-tutorial/
To use dialog/Alertdialog with the floating action button you're using, try changing your onClick(View v) from this
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
to
AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());
In my Activity I have one ImageView
XML Code:
<!-- FOTO LATERAL! -->
<ImageView
android:id="#+id/simbolo_raca"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
<Button
android:id="#+id/button_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Ok. Seguinte Pergunta"
android:layout_gravity="right"
android:gravity="right"
android:paddingRight="30dip">
</LinearLayout>
This is the Activity:
public class QuestionarioActivityRaca extends Activity{
ImageView simbolo;
int position;
Button B_Save;
List<Drawable> List_imagens = new ArrayList<Drawable>();
#Override
public void onCreate(Bundle savedInstanceState) {
simbolo = (ImageView) findViewById(R.id.simbolo_raca);
B_Save = (Button) findViewById(R.id.button_save);
position = 0;
List_imagens.add(getResources().getDrawable(R.drawable.p1));
List_imagens.add(getResources().getDrawable(R.drawable.p2));
List_imagens.add(getResources().getDrawable(R.drawable.p3));
List_imagens.add(getResources().getDrawable(R.drawable.p4));
loadNewImage(position);
B_Save.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
loadNewImage(position);
position++;
}
});
}
// I Use this method to load the Image
public loadNewImage(int Page)
{
new Thread(new Runnable(){
#Override
public void run(){
simbolo.post(new Runnable(){
#Override
public void run()
{
simbolo.setImageDrawable(List_imagens.get(Page));
}
});
}
}).start();
}
The first time I call this method, (position = 0) the image doesnt loads. After that, the image is loading correctly.
How I have to do to load the image the first time?
(I can not load the image in the XML using android:src="#drawable/x") because the image could be different anytime.
EDITED!
There are a number of issues with the code sample you posted.
like writing int with a capital I.
Int position;
Not sending in a parameter with your method in the onClick:
public void onClick(View v) {
loadNewImage();
}
And several more in both your XML and code.
Is there a spesific reason you want to run a new thread every time for this task?
If you really need a thread, you have to declare your int as final in the method.
However, you should get your desired result with the code-sample below.
You have to modify your onClick to send the appropriate int for whatever drawable you want to use.
Good luck.
public class testactivity extends ActionBarActivity {
ImageView simbolo;
int position;
Button B_Save;
List<Drawable> List_imagens = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_testactivity);
simbolo = (ImageView) findViewById(R.id.simbolo_raca);
B_Save = (Button) findViewById(R.id.button_save);
position = 0;
List_imagens.add(getResources().getDrawable(R.drawable.ic_drawer));
List_imagens.add(getResources().getDrawable(R.drawable.ic_check));
List_imagens.add(getResources().getDrawable(R.drawable.ic_action_add_package));
List_imagens.add(getResources().getDrawable(R.drawable.ic_action_exception));
loadNewImage(position);
final Random rand = new Random();
B_Save.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
loadNewImage(rand.nextInt(4));
}
});
}
public void loadNewImage(final int page)
{
simbolo.setImageDrawable(List_imagens.get(page));
}
}
first of all before the button gets clicked you call this function to load the initial image with loadNewImage(position); when the button is clicked you call this function loadNewImage(); i am guessing it works when the button is clicked because this loadNewImage(); method is ok and loadNewImage(int page); is not ok, because they are two different function.
Solving your problem if you want an integer object use Integer position or else go with int position and please let go of the thread. now your code will work
I am having weird problems with Android GridView. I create a 3x4 grid and insert buttons into that grid. I want the background of the button to change when the user clicks that button. And this is working just fine for all buttons except the first one (the one with index 0 - top left). OnClick event listener doesn't fire at all for that button no matter what I do.
Here is the code where I create the view:
public View getView(int position, View convertView, ViewGroup parent) {
Button imageView;
if (convertView == null) { // if it's not recycled, initialize some attributes
Log.w("NOVO", "narejena nova celica");
imageView = new Button(mContext);
imageView.setPadding(8, 8, 8, 8);
} else {
Log.w("STARO", "stara celica");
imageView = (Button) convertView;
}
imageView.setEnabled(true);
int visina = parent.getHeight();
int sirina = parent.getWidth();
float dip = mContext.getResources().getDisplayMetrics().density;
float margin = 10*dip;
int view_height = (int)(visina - 3*margin)/4;
int view_width = (int)(sirina - 2*margin)/3;
int view_dim = 0;
if (view_height <= view_width)
view_dim = view_height;
else
view_dim = view_width;
imageView.setLayoutParams(new GridView.LayoutParams(view_dim, view_dim));
imageView.setId(position);
imageView.setOnClickListener(celice.get(position));
/*imageView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//Toast toast = Toast.makeText(mContext, v.getId() + "bla", Toast.LENGTH_SHORT);
//toast.show();
celice.get(v.getId()).celicaVisible(4000);
}});*/
celice.get(position).id = position;
celice.get(position).setButton(imageView);
return imageView;
}
If I replace
imageView = (Button) convertView;
with
imageView = new Button(mContext);
then the onClick() gets fired but the background still doesn't change. All the other buttons are working as expected.
And here is the custom class "Celica" that takes care of the actual work - changing the background...
public class Celica implements OnClickListener {
public boolean odkrit;
public boolean najden;
public int id;
public Drawable slikca1, slikca2;
public Celica par;
private Timer tim;
public Button but;
public Context con;
static int buttonsVisible = 0;
Celica(Drawable s1, Drawable s2) {
this.slikca1 = s1;
this.slikca2 = s2;
}
void celicaVisible(int millis) {
if (odkrit)
return;
Log.w("TEST", "prizganih " + buttonsVisible);
if (buttonsVisible >= 2)
return;
odkrit = true;
buttonsVisible++;
tim = new Timer();
tim.schedule(new timerDone(), millis);
((Activity)con).runOnUiThread(new Runnable() {
#Override
public void run() {
but.setBackground(slikca2);
}
});
}
void setButton(Button b) {
but = b;
((Activity)con).runOnUiThread(new Runnable() {
#Override
public void run() {
but.setBackground(slikca1);
}
});
}
class timerDone extends TimerTask {
#Override
public void run() {
if (!najden) {
odkrit = false;
((Activity)con).runOnUiThread(new Runnable() {
#Override
public void run() {
but.setBackground(slikca1);
}
});
}
buttonsVisible--;
tim.cancel();
}
}
#Override
public void onClick(View v) {
celicaVisible(4000);
}
}
In Android, ID of any View must be non zero and non negative number. means View ID should be > 0. and there is problem, when you are setting ID to the Button like
imageView.setId(position)
here ID of a button will be zero when position is zero(means first item). may be due to this, First Button's OnClickListener is not getting fired...try setting a ID that is greater than zero to Button and try once...
you can write like
imageView.setId(position+1) to ensure ID > 0
I actually figured it out. Everything works if I use the view that gets provided by the onClick() method instead of saving the actual button at the creation of the Celica object.
So basically adding:
but = (Button) v;
to the onClick() method solved the problem.