I made a custom keyboard. When you long press a key, a PopupWindow shows some extra choices above the key. The problem is that in API 28, this popup gets clipped (or even completely hidden for the top row).
I had solved this problem for API < 28 with
popupWindow.setClippingEnabled(false);
However, with API 28 the problem has come back. Here is more of the code:
private void layoutAndShowPopupWindow(Key key, int xPosition) {
popupWindow = new PopupWindow(popupView,
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
popupWindow.setClippingEnabled(false);
int location[] = new int[2];
key.getLocationInWindow(location);
int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
popupView.measure(measureSpec, measureSpec);
int popupWidth = popupView.getMeasuredWidth();
int spaceAboveKey = key.getHeight() / 4;
int x = xPosition - popupWidth / popupView.getChildCount() / 2;
int screenWidth = getScreenWidth();
if (x < 0) {
x = 0;
} else if (x + popupWidth > screenWidth) {
x = screenWidth - popupWidth;
}
int y = location[1] - popupView.getMeasuredHeight() - spaceAboveKey;
popupWindow.showAtLocation(key, Gravity.NO_GRAVITY, x, y);
}
Did something happen to no longer allow third party keyboards to show content outside of the keyboard view? (This is how it is in iOS.)
What do I need to do to get the PopupWindow to stop being clipped?
Updated to show a more tailored approach.Updated to work with windowSoftInputMode="adjustResize".
It looks like clipping outside of windows may be a new fact of Android life although I have not found documentation to that effect. Regardless, the following method may be the preferred way to go and is, I believe, standard although not very well documented.
In the following, MyInputMethodService instantiates a keyboard that has eight keys on the bottom and an empty view strip above where popups are displayed for the top row of keys. When a key is pressed, the key value is shown in a popup window above the key for the duration of the key press. Since the empty view above the keys encloses the popups, clipping does not occur. (Not a very useful keyboard, but it makes the point.)
The button and "Low text" EditText are under the top view strip. Invocation of onComputeInsets() permits touches on the keyboard keys but disallows keyboard touches in the empty area covered by the inset. In this area, touches are passed down to the underlying views - here the "Low text" EditText and a Button that displays "OK!" when clicked.
"Gboard" seems to work in a similar fashion but uses a sister FrameLayout to display the popups with translation. Here is what a "4" popup looks like in the Layout Inspector for "Gboard".
MyInputMethodService
public class MyInputMethodService extends InputMethodService
implements View.OnTouchListener {
private View mTopKey;
private PopupWindow mPopupWindow;
private View mPopupView;
#Override
public View onCreateInputView() {
final ConstraintLayout keyboardView = (ConstraintLayout) getLayoutInflater().inflate(R.layout.keyboard, null);
mTopKey = keyboardView.findViewById(R.id.a);
mTopKey.setOnTouchListener(this);
keyboardView.findViewById(R.id.b).setOnTouchListener(this);
keyboardView.findViewById(R.id.c).setOnTouchListener(this);
keyboardView.findViewById(R.id.d).setOnTouchListener(this);
keyboardView.findViewById(R.id.e).setOnTouchListener(this);
keyboardView.findViewById(R.id.f).setOnTouchListener(this);
keyboardView.findViewById(R.id.g).setOnTouchListener(this);
keyboardView.findViewById(R.id.h).setOnTouchListener(this);
mPopupView = getLayoutInflater().inflate(R.layout.popup, keyboardView, false);
int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
mPopupView.measure(measureSpec, measureSpec);
mPopupWindow = new PopupWindow(mPopupView, ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
return keyboardView;
}
#Override
public void onComputeInsets(InputMethodService.Insets outInsets) {
// Do the standard stuff.
super.onComputeInsets(outInsets);
// Only the keyboard are with the keys is touchable. The rest should pass touches
// through to the views behind. contentTopInsets set to play nice with windowSoftInputMode
// defined in the manifest.
outInsets.visibleTopInsets = mTopKey.getTop();
outInsets.contentTopInsets = mTopKey.getTop();
}
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
layoutAndShowPopupWindow((TextView) v);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mPopupWindow.dismiss();
break;
}
return true;
}
private void layoutAndShowPopupWindow(TextView key) {
((TextView) mPopupView.findViewById(R.id.popupKey)).setText(key.getText());
int x = key.getLeft() + (key.getWidth() - mPopupView.getMeasuredWidth()) / 2;
int y = key.getTop() - mPopupView.getMeasuredHeight();
mPopupWindow.showAtLocation(key, Gravity.NO_GRAVITY, x, y);
}
}
keyboard.xml
The View is defined solely to give the popups a place to expand into and has no other purpose.
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="#+id/a" />
<Button
android:id="#+id/a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="A"
app:layout_constraintBottom_toTopOf="#+id/e"
app:layout_constraintEnd_toStartOf="#+id/b"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="#+id/b"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="B"
app:layout_constraintBottom_toTopOf="#+id/f"
app:layout_constraintEnd_toStartOf="#+id/c"
app:layout_constraintStart_toEndOf="#+id/a" />
<Button
android:id="#+id/c"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="C"
app:layout_constraintBottom_toTopOf="#+id/g"
app:layout_constraintEnd_toStartOf="#+id/d"
app:layout_constraintStart_toEndOf="#+id/b" />
<Button
android:id="#+id/d"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="D"
app:layout_constraintBottom_toTopOf="#+id/h"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/c" />
<Button
android:id="#+id/e"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="E"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/f"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="#+id/f"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="F"
app:layout_constraintEnd_toStartOf="#+id/g"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="#+id/e"
app:layout_constraintTop_toTopOf="#+id/e" />
<Button
android:id="#+id/g"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G"
app:layout_constraintEnd_toStartOf="#+id/h"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="#+id/f"
app:layout_constraintTop_toTopOf="#+id/e" />
<Button
android:id="#+id/h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="H"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="#+id/g"
app:layout_constraintTop_toTopOf="#+id/g" />
</android.support.constraint.ConstraintLayout>
popup.xml
Just the popup.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:background="#android:color/black"
android:gravity="center"
android:orientation="vertical"
android:padding="3dp">
<TextView
android:id="#+id/popupKey"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="A"
android:textColor="#android:color/white" />
</LinearLayout>
activity_main
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="High text"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="20dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<EditText
android:id="#+id/editText"
android:layout_width="133dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:ems="10"
android:inputType="textPersonName"
android:hint="Low text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/button" />
</android.support.constraint.ConstraintLayout>
A general idea to show popup views is to create them using WindowManager which has not limitations of PopupWindow.
I assume that the InputMethodService is responsible to show the popup view.
As showing such window needs to get overlay permission in API 23 and higher, we need to make a temp Activity to do this for us . The result of getting permission would be delivered to the InputMethodService using an EventBus event. You can check the overlay permission where you want according to architecture (for example every time the keyboard goes up).
Here is an implementation of this idea which may need some manipulations to work exactly you want. I hope it helps.
MyInputMethodService.java
import android.content.Intent;
import android.inputmethodservice.InputMethodService;
import android.os.Build;
import android.provider.Settings;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
public class MyInputMethodService extends InputMethodService {
private FloatViewManager mFloatViewManager;
#Override
public void onCreate() {
super.onCreate();
EventBus.getDefault().register(this);
checkDrawOverlayPermission();
}
#Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
private boolean checkDrawOverlayPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
Intent intent = new Intent(this, CheckPermissionActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
return false;
} else {
return true;
}
}
private void showPopup(Key key, int xPosition){
mFloatViewManager = new FloatViewManager(this);
if (checkDrawOverlayPermission()) {
mFloatViewManager.showFloatView(key, xPosition);
}
}
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(CanDrawOverlaysEvent event) {
if (event.isAllowed()) {
mFloatViewManager.showFloatView(key, xPosition);
} else {
// Maybe show an error
}
}
}
FloatViewManager.java
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import static android.content.Context.WINDOW_SERVICE;
public class FloatViewManager {
private WindowManager mWindowManager;
private View mFloatView;
private WindowManager.LayoutParams mFloatViewLayoutParams;
#SuppressLint("InflateParams")
public FloatViewManager(Context context) {
mWindowManager = (WindowManager) context.getSystemService(WINDOW_SERVICE);
LayoutInflater inflater = LayoutInflater.from(context);
mFloatView = inflater.inflate(R.layout.float_view_layout, null);
// --------- do initializations:
TextView textView = mFloatView.findViewById(R.id.textView);
// ...
// ---------
mFloatViewLayoutParams = new WindowManager.LayoutParams();
mFloatViewLayoutParams.format = PixelFormat.TRANSLUCENT;
mFloatViewLayoutParams.flags = WindowManager.LayoutParams.FORMAT_CHANGED;
mFloatViewLayoutParams.type = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
: WindowManager.LayoutParams.TYPE_PHONE;
mFloatViewLayoutParams.gravity = Gravity.NO_GRAVITY;
mFloatViewLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mFloatViewLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
}
public void dismissFloatView() {
mWindowManager.removeViewImmediate(mFloatView);
}
public void showFloatView(Key key, int xPosition) {
// calculate x and y position as you did instead of 0
mFloatViewLayoutParams.x = 0;
mFloatViewLayoutParams.y = 0;
mWindowManager.addView(mFloatView, mFloatViewLayoutParams);
mWindowManager.updateViewLayout(mFloatView, mFloatViewLayoutParams);
}
}
CheckPermissionActivity.java
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import org.greenrobot.eventbus.EventBus;
public class CheckPermissionActivity extends AppCompatActivity {
private static final int REQUEST_CODE_DRAW_OVERLAY_PERMISSION = 5;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE_DRAW_OVERLAY_PERMISSION);
} else {
finish();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_DRAW_OVERLAY_PERMISSION) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(this)) {
EventBus.getDefault().post(new CanDrawOverlaysEvent(true));
} else {
EventBus.getDefault().post(new CanDrawOverlaysEvent(false));
}
finish();
}
}
}
CanDrawOverlaysEvent.java
public class CanDrawOverlaysEvent {
private boolean mIsAllowed;
public CanDrawOverlaysEvent(boolean isAllowed) {
mIsAllowed = isAllowed;
}
public boolean isAllowed() {
return mIsAllowed;
}
}
build.gradle
dependencies {
implementation 'org.greenrobot:eventbus:3.1.1'
}
I have fixed that with LatinIME(AOSP) like:
my input view layout xml file is
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.my.android.ime.InputView
android:id="#+id/input_view"
android:background="#color/black"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
copy function "updateSoftInputWindowLayoutParameters" from LatinIME.java
private void updateSoftInputWindowLayoutParameters() {
// Override layout parameters to expand {#link SoftInputWindow} to the entire screen.
// See {#link InputMethodService#setinputView(View)} and
// {#link SoftInputWindow#updateWidthHeight(WindowManager.LayoutParams)}.
final Window window = getWindow().getWindow();
ViewLayoutUtils.updateLayoutHeightOf(window, LayoutParams.MATCH_PARENT);
// This method may be called before {#link #setInputView(View)}.
if (mInputView != null) {
// In non-fullscreen mode, {#link InputView} and its parent inputArea should expand to
// the entire screen and be placed at the bottom of {#link SoftInputWindow}.
// In fullscreen mode, these shouldn't expand to the entire screen and should be
// coexistent with {#link #mExtractedArea} above.
// See {#link InputMethodService#setInputView(View) and
// com.android.internal.R.layout.input_method.xml.
final int layoutHeight = isFullscreenMode()
? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT;
final View inputArea = window.findViewById(android.R.id.inputArea);
ViewLayoutUtils.updateLayoutHeightOf(inputArea, layoutHeight);
ViewLayoutUtils.updateLayoutGravityOf(inputArea, Gravity.BOTTOM);
ViewLayoutUtils.updateLayoutHeightOf(mInputView, layoutHeight);
}
}
Overriding function: "updateFullscreenMode", "setInputView", "onComputeInsets" and copy code from LatinIME.java - finally modify the code like
private View mInputView;
private InsetsUpdater mInsetsUpdater;
...
#Override
public void onStartInputView(EditorInfo info, boolean restarting) {
...
updateFullscreenMode();
super.onStartInputView(info, restarting);
}
#Override
public void updateFullscreenMode() {
super.updateFullscreenMode();
updateSoftInputWindowLayoutParameters();
}
#Override
public void setInputView(final View view) {
super.setInputView(view);
mInputView = view;
mInsetsUpdater = ViewOutlineProviderCompatUtils.setInsetsOutlineProvider(view);
updateSoftInputWindowLayoutParameters();
//mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view);
//if (hasSuggestionStripView()) {
// mSuggestionStripView.setListener(this, view);
//}
}
#Override
public void onComputeInsets(final InputMethodService.Insets outInsets) {
super.onComputeInsets(outInsets);
// This method may be called before {#link #setInputView(View)}.
if (mInputView == null) {
return;
}
//final SettingsValues settingsValues = mSettings.getCurrent();
//final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView();
final View visibleKeyboardView = mInputView.findViewById(R.id.input_view);
//if (visibleKeyboardView == null || !hasSuggestionStripView()) {
// return;
//}
final int inputHeight = mInputView.getHeight();
//if (isImeSuppressedByHardwareKeyboard() && !visibleKeyboardView.isShown()) {
// // If there is a hardware keyboard and a visible software keyboard view has been hidden,
// // no visual element will be shown on the screen.
// outInsets.contentTopInsets = inputHeight;
// outInsets.visibleTopInsets = inputHeight;
// mInsetsUpdater.setInsets(outInsets);
// return;
//}
//final int suggestionsHeight = (!mKeyboardSwitcher.isShowingEmojiPalettes()
// && mSuggestionStripView.getVisibility() == View.VISIBLE)
// ? mSuggestionStripView.getHeight() : 0;
final int visibleTopY = inputHeight - visibleKeyboardView.getHeight();// - suggestionsHeight;
//mSuggestionStripView.setMoreSuggestionsHeight(visibleTopY);
// Need to set expanded touchable region only if a keyboard view is being shown.
if (visibleKeyboardView.isShown()) {
final int touchLeft = 0;
//final int touchTop = mKeyboardSwitcher.isShowingMoreKeysPanel() ? 0 : visibleTopY;
final int touchTop = visibleTopY;
final int touchRight = visibleKeyboardView.getWidth();
final int touchBottom = inputHeight;
outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
outInsets.touchableRegion.set(touchLeft, touchTop, touchRight, touchBottom);
Log.i(TAG, "onComputeInsets: left=" + touchLeft + ", top=" + touchTop + ", right=" + touchRight + ", bottom=" + touchBottom);
}
Log.i(TAG, "onComputeInsets: visibleTopY=" + visibleTopY);
outInsets.contentTopInsets = visibleTopY;
outInsets.visibleTopInsets = visibleTopY;
mInsetsUpdater.setInsets(outInsets);
}
copy file "ViewLayoutUtils.java", "ViewOutlineProviderCompatUtils.java", "ViewOutlineProviderCompatUtilsLXX.java" from LatinIME(AOSP) package and modify package name
The simplest solution to this is to not attach popup window to keyboard decorview:
popupWindow.setAttachedInDecor(false);
Related
So this is my code. I am trying to create what looks like a PPT slideshow of images with their title and description in my application.
I have the below code and I am stuck on how to allow the user to:
When he gets into the slideshow he would get the images, with a title and text "description" for each.
when "ENTER" is pressed set the text on that image to invisible.
When the user navigates to the right, the next view(s) would contain a text and Image.
-When the user navigates back left the text view that he set invisible should still be invisible and the has the option to get it to become visible again by just pressing enter.
The last part is what I am stuck on, as I am not able to figure out how each "Linear Layout" inside my viewpager would remember its visibility and act accordingly. (i.e. gets back to visible once it was invisible and vice versa).
I appreciate your input.
Please find the code below:
My ViewPager XML file:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/fragment_pager_main_parent"
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"
android:background="#drawable/default_background"
tools:context=".PowerPointActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/fragment_pager_viewPager2"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
My views XML file:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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:id="#+id/fragment_pager_item_parent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/fragment_pager_item_imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:scaleType="fitXY"/>
<LinearLayout
android:id="#+id/fragment_pager_item_main_child"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="textStart"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="#+id/fragment_pager_item_parent"
app:layout_constraintStart_toStartOf="#+id/fragment_pager_item_parent">
<!-- the linear layout below was not initially here but I was trying to have a layout for each child.-->
<LinearLayout
android:id="#+id/fragment_pager_item_child"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="textStart"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="#+id/fragment_pager_item_parent"
app:layout_constraintStart_toStartOf="#+id/fragment_pager_item_parent">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/fragment_pager_item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:gravity="top"
android:textColor="#FFFFFF"
android:textSize="12dp"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="#+id/fragment_pager_item_child"
app:layout_constraintTop_toTopOf="#+id/fragment_pager_item_child"
app:layout_constraintVertical_bias="0.0"
tools:text="Topic" />
<TextView
android:id="#+id/fragment_pager_item_about"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="top"
android:layout_marginStart="10dp"
android:elegantTextHeight="true"
android:text="#string/about_topic"
android:textColor="#ffffff"
android:textSize="12sp"
app:layout_constraintHorizontal_bias="10"
app:layout_constraintStart_toStartOf="#+id/fragment_pager_item_title"
app:layout_constraintTop_toBottomOf="#id/fragment_pager_item_title"
app:layout_dodgeInsetEdges="left" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
My Adapter Class:
public class PowerpointAdapter extends RecyclerView.Adapter{
private List<Image> mPowerPointList;
static class PagesViewHolder extends RecyclerView.ViewHolder {
private ImageView mViewImageView;
private TextView mTitleView;
private TextView mDescriptionView;
private LinearLayout mLinearLayout;
public PagesViewHolder(#NonNull View itemView) {
super(itemView);
Timber.i("PagesViewHolder");
mViewImageView = itemView.findViewById(R.id.fragment_pager_item_imageView);
mTitleView = itemView.findViewById((R.id.fragment_pager_item_title));
mDescriptionView = itemView.findViewById(R.id.fragment_pager_item_about);
mLinearLayout = itemView.findViewById(R.id.fragment_pager_item_child);
}
}
public PowerpointAdapter(List<Image> mPowerPointList) { this.mPowerPointList = mPowerPointList;}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
Timber.i("onCreateViewHolder");
View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.fragment_pager_item, parent, false);
return new PagesViewHolder(mView);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Timber.i("onBindViewHolder");
PagesViewHolder mViewHolder = (PagesViewHolder) holder;
Image powerpoint = mPowerPointList.get(position);
mViewHolder.mViewImageView.setImageResource(powerpoint.getTrialImage());
mViewHolder.mTitleView.setText(powerpoint.getTitle());
mViewHolder.mDescriptionView.setText(powerpoint.getDescription());
mViewHolder.mLinearLayout.setVisibility(View.VISIBLE);
}
#Override
public int getItemCount() {
Timber.i("getItemCount");
return mPowerPointList.size();}
My activity class:
// for a one image display, the image can be treated similarly to a powerpoint.
public class PowerPointActivity extends Activity {
private ViewPager2 viewPager2;
#Override
protected void onCreate(Bundle savedInstanceState) {
Timber.i("onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_pager_main);
ConstraintLayout mLayout = findViewById(R.id.fragment_pager_main_parent);
mLayout.setBackgroundResource(R.color.Transparent_black);
viewPager2 = findViewById(R.id.fragment_pager_viewPager2);
setUpPagerAdapter();
}
/**
* this method will set up the adapter
*/
private void setUpPagerAdapter() {
Timber.i("SetUpPagerAdapter");
PowerpointAdapter pagerAdapter = new PowerpointAdapter(fetchDummyData());
viewPager2.setAdapter(pagerAdapter);
viewPager2.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
}
/**
* #return this method will return dummy data in form of list
*/
private List<Image> fetchDummyData() {
Timber.i("fetchDummyData in Power Point Activity");
List<Image> powerpointList = new ArrayList<>();
String[] dummyArrDescriptions = getResources().getStringArray(R.array.array_str_descriptions_for_powerPoints);
String[] dummyArrTitles = getResources().getStringArray(R.array.array_str_titles_for_powerPoints);
for (int index = 0; index < dummyArrTitles.length; ++index) {
Image image = new Image(dummyArrTitles[index], dummyArrDescriptions[index], R.drawable.ppt_image);
powerpointList.add(image);
}
return powerpointList;
}
/**
* this method handles slideshow navigation
*/
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
Timber.i("OnKeyDown");
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT: {
viewPager2.setCurrentItem(viewPager2.getCurrentItem() - 1);
Toast.makeText(PowerPointActivity.this, "Left pressed!", Toast.LENGTH_SHORT).show();
break;
}
case KeyEvent.KEYCODE_DPAD_RIGHT: {
viewPager2.setCurrentItem(viewPager2.getCurrentItem() + 1);
Toast.makeText(PowerPointActivity.this, "Right pressed!", Toast.LENGTH_SHORT).show();
break;
}
case KeyEvent.KEYCODE_BACK: {
Toast.makeText(PowerPointActivity.this, "Back pressed!", Toast.LENGTH_SHORT).show();
super.onBackPressed();
break;
}
// todo: Fix the visibility of each page description
case KeyEvent.KEYCODE_ENTER: {
if (findViewById(R.id.fragment_pager_item_child).isShown()) {
Timber.i("It is shown. Setting it to not shown! "+ viewPager2.getCurrentItem() + " now.");
// here this specific child's text should be set to invisible and remembered. findViewById(R.id.fragment_pager_item_child).setVisibility(View.INVISIBLE);
LinearLayout mLayout = findViewById(R.id.fragment_pager_item_child);
Animation outFade = AnimationUtils.loadAnimation(getApplicationContext(),R.anim.fade_out);
mLayout.startAnimation(outFade);
mLayout.startAnimation(outFade);
break;
} else {
Timber.i("It is now not shown. Setting it to shown! " + viewPager2.getCurrentItem() + " now.");
LinearLayout mLayout = findViewById(R.id.fragment_pager_item_child);
Animation aniFade = AnimationUtils.loadAnimation(getApplicationContext(),R.anim.fade_in);
mLayout.startAnimation(aniFade);
mLayout.startAnimation(aniFade);
findViewById(R.id.fragment_pager_item_child).setVisibility(View.VISIBLE);
break;
}
}
}
return false;
}
}
Thank you!
From the Android documentation we can find that the Adapter holds the following responsibilities:
Acts as a bridge between an AdapterView and the underlying data for that view.
Provides access to the data items
Responsible for making a View for each item in the data set.
So basically it's not the View's job to remember the child's visibility and act upon it. It's PowerpointAdapter responsibility.
You need a status field in your Image object and change it when the view visibility is changed. Also, don't forget to set the view visibility according to this status variable.
For example:
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Timber.i("onBindViewHolder");
PagesViewHolder mViewHolder = (PagesViewHolder) holder;
Image powerpoint = mPowerPointList.get(position);
mViewHolder.mViewImageView.setImageResource(powerpoint.getTrialImage());
mViewHolder.mTitleView.setText(powerpoint.getTitle());
mViewHolder.mDescriptionView.setText(powerpoint.getDescription());
if(powerpoint.isLayoutVisibile){
mViewHolder.mLinearLayout.setVisibility(View.VISIBLE);
}else{
mViewHolder.mLinearLayout.setVisibility(View.GONE);
}
}
And Inside your ClickListener you should change this field and notify the PowerpointAdapter of these changes.
I have a view with a Edittext field on top of an ImageView. When the keyboard comes up I want the window to resize so that EditText is no longer hidden by the keyboard. In the AndroidManifest file I declared android:windowSoftInputMode="adjustResize" and the screen is resized but the issue is that I want the ImageView to not be re-sized.
How can I make the ImageView unaffected?
Could I inflate an additional layout with just the ImageView or will the resize still affect it?
The full solution involves a few key points
Use RelativeLayout, so that Views can be setup to overlap one another
Align the EditText with the bottom of the Windows using android:layout_alignParentBottom="true"
Use android:windowSoftInputMode="adjustResize" in your manifest, so that the bottom of the Window changes when the keyboard pops up (as you mentioned)
Put the ImageView inside a ScrollView so that the ImageView can be larger than the Window, and disable scrolling on the ScrollView by using ScrollView#setEnabled(false)
Here is the layout file
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.so3.MainActivity">
<ScrollView
android:id="#+id/scroll"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="#drawable/stickfigures"/>
</ScrollView>
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#android:color/holo_blue_bright"
android:text="Please enter text"
android:textSize="40sp"
android:gravity="center_horizontal"/>
</RelativeLayout>
Here is my Activity
package com.so3;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ScrollView;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ScrollView sv = (ScrollView)findViewById(R.id.scroll);
sv.setEnabled(false);
}
}
My AndroidManifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.so3" >
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.so3.MainActivity"
android:windowSoftInputMode="adjustResize"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Screen shots of my solution
Adding ScrollView was making my image scrollable which I wanted to avoid so I used this samples-keyboardheight calculator and onKeyboardHeightChanged recalculated position of the bottom Edittext placed it above Keyboard and used this flag in Manifest.
android:windowSoftInputMode="adjustNothing|stateHidden"
Here is KeyboardHeightProvider :
import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager.LayoutParams;
import android.widget.PopupWindow;
/**
* The keyboard height provider, this class uses a PopupWindow
* to calculate the window height when the floating keyboard is opened and closed.
*/
public class KeyboardHeightProvider extends PopupWindow {
/** The tag for logging purposes */
private final static String TAG = "sample_KeyboardHeightProvider";
/** The keyboard height observer */
private KeyboardHeightObserver observer;
/** The cached landscape height of the keyboard */
private int keyboardLandscapeHeight;
/** The cached portrait height of the keyboard */
private int keyboardPortraitHeight;
/** The view that is used to calculate the keyboard height */
private View popupView;
/** The parent view */
private View parentView;
/** The root activity that uses this KeyboardHeightProvider */
private Activity activity;
/**
* Construct a new KeyboardHeightProvider
*
* #param activity The parent activity
*/
public KeyboardHeightProvider(Activity activity) {
super(activity);
this.activity = activity;
LayoutInflater inflator = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
this.popupView = inflator.inflate(R.layout.popupwindow, null, false);
setContentView(popupView);
setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_RESIZE | LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
parentView = activity.findViewById(android.R.id.content);
setWidth(0);
setHeight(LayoutParams.MATCH_PARENT);
popupView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (popupView != null) {
handleOnGlobalLayout();
}
}
});
}
/**
* Start the KeyboardHeightProvider, this must be called after the onResume of the Activity.
* PopupWindows are not allowed to be registered before the onResume has finished
* of the Activity.
*/
public void start() {
if (!isShowing() && parentView.getWindowToken() != null) {
setBackgroundDrawable(new ColorDrawable(0));
showAtLocation(parentView, Gravity.NO_GRAVITY, 0, 0);
}
}
/**
* Close the keyboard height provider,
* this provider will not be used anymore.
*/
public void close() {
this.observer = null;
dismiss();
}
/**
* Set the keyboard height observer to this provider. The
* observer will be notified when the keyboard height has changed.
* For example when the keyboard is opened or closed.
*
* #param observer The observer to be added to this provider.
*/
public void setKeyboardHeightObserver(KeyboardHeightObserver observer) {
this.observer = observer;
}
/**
* Get the screen orientation
*
* #return the screen orientation
*/
private int getScreenOrientation() {
return activity.getResources().getConfiguration().orientation;
}
/**
* Popup window itself is as big as the window of the Activity.
* The keyboard can then be calculated by extracting the popup view bottom
* from the activity window height.
*/
private void handleOnGlobalLayout() {
Point screenSize = new Point();
activity.getWindowManager().getDefaultDisplay().getSize(screenSize);
Rect rect = new Rect();
popupView.getWindowVisibleDisplayFrame(rect);
// REMIND, you may like to change this using the fullscreen size of the phone
// and also using the status bar and navigation bar heights of the phone to calculate
// the keyboard height. But this worked fine on a Nexus.
int orientation = getScreenOrientation();
int keyboardHeight = screenSize.y - rect.bottom;
if (keyboardHeight == 0) {
notifyKeyboardHeightChanged(0, orientation);
}
else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
this.keyboardPortraitHeight = keyboardHeight;
notifyKeyboardHeightChanged(keyboardPortraitHeight, orientation);
}
else {
this.keyboardLandscapeHeight = keyboardHeight;
notifyKeyboardHeightChanged(keyboardLandscapeHeight, orientation);
}
}
/**
*
*/
private void notifyKeyboardHeightChanged(int height, int orientation) {
if (observer != null) {
observer.onKeyboardHeightChanged(height, orientation);
}
}
public interface KeyboardHeightObserver {
void onKeyboardHeightChanged(int height, int orientation);
}
}
popupwindow.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/popuplayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/transparent"
android:orientation="horizontal"/>
Here is MainActivity.java :
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
public class MainActivity extends AppCompatActivity implements KeyboardHeightProvider.KeyboardHeightObserver {
private KeyboardHeightProvider keyboardHeightProvider;
private ViewGroup relativeView;
private float initialY;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
keyboardHeightProvider = new KeyboardHeightProvider(this);
relativeView = findViewById(R.id.bottomEditor);
relativeView.post(() -> initialY = relativeView.getY());
View view = findViewById(R.id.activitylayout);
view.post(() -> keyboardHeightProvider.start());
}
#Override
public void onKeyboardHeightChanged(int height, int orientation) {
if(height == 0){
relativeView.setY(initialY);
relativeView.requestLayout();
}else {
float newPosition = initialY - height;
relativeView.setY(newPosition);
relativeView.requestLayout();
}
}
#Override
public void onPause() {
super.onPause();
keyboardHeightProvider.setKeyboardHeightObserver(null);
}
#Override
public void onResume() {
super.onResume();
keyboardHeightProvider.setKeyboardHeightObserver(this);
}
#Override
public void onDestroy() {
super.onDestroy();
keyboardHeightProvider.close();
}
}
activity_main.xml :
<?xml version="1.0" encoding="utf-8"?>
<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:id="#+id/activitylayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/imageView2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
/>
<RelativeLayout
android:id="#+id/bottomEditor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
>
<EditText
android:id="#+id/edit_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:layout_toStartOf="#+id/btn_send"
android:hint="Add caption"
android:paddingBottom="12dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingStart="8dp"
android:paddingTop="12dp"
/>
<ImageButton
android:id="#+id/btn_send"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignBottom="#+id/edit_message"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
app:srcCompat="#android:drawable/ic_menu_send"
/>
</RelativeLayout>
</RelativeLayout>
P.S. : Keyboard height calculation code is copied from siebeprojects
Here is demo example app of implementation.
final View activityRootView = findViewById(R.id.mainScroll);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int heightView = activityRootView.getHeight();
int widthView = activityRootView.getWidth();
if (1.0 * widthView / heightView > 1) {
Log.d("keyboarddddd visible", "no");
relativeLayoutForImage.setVisibility(View.GONE);
relativeLayoutStatic.setVisibility(View.GONE);
//Make changes for Keyboard not visible
} else {
Log.d("keyboarddddd visible ", "yes");
relativeLayoutForImage.setVisibility(View.VISIBLE);
relativeLayoutStatic.setVisibility(View.VISIBLE);
//Make changes for keyboard visible
}
}
});
For me i did not want to assume that keyboards heights are a certain measurement. Whatever view your concerned about make a onTouchListener and then do this:
setOnTouchListener(new OnTouchListener() {
Runnable shifter=new Runnable(){
public void run(){
try {
int[] loc = new int[2];
//get the location of someview which gets stored in loc array
findViewById(R.id.someview).getLocationInWindow(loc);
//shift so user can see someview
myscrollView.scrollTo(loc[0], loc[1]);
}
catch (Exception e) {
e.printStackTrace();
}
}}
};
Rect scrollBounds = new Rect();
View divider=findViewById(R.id.someview);
myscollView.getHitRect(scrollBounds);
if (!divider.getLocalVisibleRect(scrollBounds)) {
// the divider view is NOT within the visible scroll window thus we need to scroll a bit.
myscollView.postDelayed(shifter, 500);
}
});
//essentially we make a runnable that scrolls to a new location of some view that you WANT visible on the screen. you execute that runnable only if its not within the scrollviews bounds (its not on the screen). This way it shifts the scrollview to the referenced view (in my case 'someview' which was a line divider).
In my opinion the easiest way to do this it is this combination of the two changes:
android:windowSoftInputMode="adjustResize"
in your AndroidManifest.xml
+
getWindow().setBackgroundDrawable(your_image_drawable);
in your activity in #onCreate() method
It works for me.
The best solution is to use a DialogFragment
Show dialog
DialogFragment.show(getSupportFragmentManager(), DialogFragment.TAG);
Full screen
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = new Dialog(getActivity(), R.style.MainDialog) { //set the style, the best code here or with me, we do not change
#Override
public void onBackPressed() {
super.onBackPressed();
getActivity().finish();
}
};
return dialog;
}
Style
<style name="MainDialog" parent="#android:style/Theme.Dialog">
<item name="android:windowBackground">#android:color/transparent</item>
<item name="android:windowFrame">#null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">false</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowContentOverlay">#null</item>
<item name="android:background">#null</item>
<item name="android:windowAnimationStyle">#null</item>
</style>
Layout Activity
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/black">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Layout dialog fragment
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/transparent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:background="#color/background_transparent_60"
android:gravity="center_vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="#dimen/spacing_1_8dp"
android:layout_marginLeft="#dimen/spacing_1_8dp"
android:layout_marginRight="#dimen/spacing_1_8dp"
android:layout_weight="1"
android:hint="#string/comment_entry_hint"
android:inputType="textMultiLine"
android:maxLines="4"
android:textColor="#color/white"
android:textColorHint="#color/secondary_text_hint"
android:textSize="#dimen/text_2_12sp" />
<ImageView
android:layout_width="#dimen/livestream_comment_height"
android:layout_height="#dimen/livestream_comment_height"
android:layout_margin="#dimen/spacing_1_8dp"
android:src="#drawable/ic_send" />
</LinearLayout>
</RelativeLayout>
The solution that worked for me was in AndroidManifest.xml in that activity tag just put
android:windowSoftInputMode="stateHidden|adjustResize|adjustNothing"
All set..Hope this will work for you.
final View activityRootView = findViewById(R.id.mainScroll);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int heightView = activityRootView.getHeight();
int widthView = activityRootView.getWidth();
if (1.0 * widthView / heightView > 1) {
Log.d("keyboarddddd visible", "no");
relativeLayoutForImage.setVisibility(View.GONE);
relativeLayoutStatic.setVisibility(View.GONE);
//Make changes for Keyboard not visible
//relativeLayoutForImage.setVisibility(View.VISIBLE);
//relativeLayoutStatic.setVisibility(View.VISIBLE);
} else {
Log.d("keyboarddddd visible ", "yes");
relativeLayoutForImage.setVisibility(View.VISIBLE);
relativeLayoutStatic.setVisibility(View.VISIBLE);
//Make changes for keyboard visible
// relativeLayoutForImage.setVisibility(View.GONE);
//relativeLayoutStatic.setVisibility(View.GONE);
}
}
});
I have a view with a Edittext field on top of an ImageView. When the keyboard comes up I want the window to resize so that EditText is no longer hidden by the keyboard. In the AndroidManifest file I declared android:windowSoftInputMode="adjustResize" and the screen is resized but the issue is that I want the ImageView to not be re-sized.
How can I make the ImageView unaffected?
Could I inflate an additional layout with just the ImageView or will the resize still affect it?
The full solution involves a few key points
Use RelativeLayout, so that Views can be setup to overlap one another
Align the EditText with the bottom of the Windows using android:layout_alignParentBottom="true"
Use android:windowSoftInputMode="adjustResize" in your manifest, so that the bottom of the Window changes when the keyboard pops up (as you mentioned)
Put the ImageView inside a ScrollView so that the ImageView can be larger than the Window, and disable scrolling on the ScrollView by using ScrollView#setEnabled(false)
Here is the layout file
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.so3.MainActivity">
<ScrollView
android:id="#+id/scroll"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="#drawable/stickfigures"/>
</ScrollView>
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#android:color/holo_blue_bright"
android:text="Please enter text"
android:textSize="40sp"
android:gravity="center_horizontal"/>
</RelativeLayout>
Here is my Activity
package com.so3;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ScrollView;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ScrollView sv = (ScrollView)findViewById(R.id.scroll);
sv.setEnabled(false);
}
}
My AndroidManifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.so3" >
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.so3.MainActivity"
android:windowSoftInputMode="adjustResize"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Screen shots of my solution
Adding ScrollView was making my image scrollable which I wanted to avoid so I used this samples-keyboardheight calculator and onKeyboardHeightChanged recalculated position of the bottom Edittext placed it above Keyboard and used this flag in Manifest.
android:windowSoftInputMode="adjustNothing|stateHidden"
Here is KeyboardHeightProvider :
import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager.LayoutParams;
import android.widget.PopupWindow;
/**
* The keyboard height provider, this class uses a PopupWindow
* to calculate the window height when the floating keyboard is opened and closed.
*/
public class KeyboardHeightProvider extends PopupWindow {
/** The tag for logging purposes */
private final static String TAG = "sample_KeyboardHeightProvider";
/** The keyboard height observer */
private KeyboardHeightObserver observer;
/** The cached landscape height of the keyboard */
private int keyboardLandscapeHeight;
/** The cached portrait height of the keyboard */
private int keyboardPortraitHeight;
/** The view that is used to calculate the keyboard height */
private View popupView;
/** The parent view */
private View parentView;
/** The root activity that uses this KeyboardHeightProvider */
private Activity activity;
/**
* Construct a new KeyboardHeightProvider
*
* #param activity The parent activity
*/
public KeyboardHeightProvider(Activity activity) {
super(activity);
this.activity = activity;
LayoutInflater inflator = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
this.popupView = inflator.inflate(R.layout.popupwindow, null, false);
setContentView(popupView);
setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_RESIZE | LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
parentView = activity.findViewById(android.R.id.content);
setWidth(0);
setHeight(LayoutParams.MATCH_PARENT);
popupView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (popupView != null) {
handleOnGlobalLayout();
}
}
});
}
/**
* Start the KeyboardHeightProvider, this must be called after the onResume of the Activity.
* PopupWindows are not allowed to be registered before the onResume has finished
* of the Activity.
*/
public void start() {
if (!isShowing() && parentView.getWindowToken() != null) {
setBackgroundDrawable(new ColorDrawable(0));
showAtLocation(parentView, Gravity.NO_GRAVITY, 0, 0);
}
}
/**
* Close the keyboard height provider,
* this provider will not be used anymore.
*/
public void close() {
this.observer = null;
dismiss();
}
/**
* Set the keyboard height observer to this provider. The
* observer will be notified when the keyboard height has changed.
* For example when the keyboard is opened or closed.
*
* #param observer The observer to be added to this provider.
*/
public void setKeyboardHeightObserver(KeyboardHeightObserver observer) {
this.observer = observer;
}
/**
* Get the screen orientation
*
* #return the screen orientation
*/
private int getScreenOrientation() {
return activity.getResources().getConfiguration().orientation;
}
/**
* Popup window itself is as big as the window of the Activity.
* The keyboard can then be calculated by extracting the popup view bottom
* from the activity window height.
*/
private void handleOnGlobalLayout() {
Point screenSize = new Point();
activity.getWindowManager().getDefaultDisplay().getSize(screenSize);
Rect rect = new Rect();
popupView.getWindowVisibleDisplayFrame(rect);
// REMIND, you may like to change this using the fullscreen size of the phone
// and also using the status bar and navigation bar heights of the phone to calculate
// the keyboard height. But this worked fine on a Nexus.
int orientation = getScreenOrientation();
int keyboardHeight = screenSize.y - rect.bottom;
if (keyboardHeight == 0) {
notifyKeyboardHeightChanged(0, orientation);
}
else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
this.keyboardPortraitHeight = keyboardHeight;
notifyKeyboardHeightChanged(keyboardPortraitHeight, orientation);
}
else {
this.keyboardLandscapeHeight = keyboardHeight;
notifyKeyboardHeightChanged(keyboardLandscapeHeight, orientation);
}
}
/**
*
*/
private void notifyKeyboardHeightChanged(int height, int orientation) {
if (observer != null) {
observer.onKeyboardHeightChanged(height, orientation);
}
}
public interface KeyboardHeightObserver {
void onKeyboardHeightChanged(int height, int orientation);
}
}
popupwindow.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/popuplayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/transparent"
android:orientation="horizontal"/>
Here is MainActivity.java :
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
public class MainActivity extends AppCompatActivity implements KeyboardHeightProvider.KeyboardHeightObserver {
private KeyboardHeightProvider keyboardHeightProvider;
private ViewGroup relativeView;
private float initialY;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
keyboardHeightProvider = new KeyboardHeightProvider(this);
relativeView = findViewById(R.id.bottomEditor);
relativeView.post(() -> initialY = relativeView.getY());
View view = findViewById(R.id.activitylayout);
view.post(() -> keyboardHeightProvider.start());
}
#Override
public void onKeyboardHeightChanged(int height, int orientation) {
if(height == 0){
relativeView.setY(initialY);
relativeView.requestLayout();
}else {
float newPosition = initialY - height;
relativeView.setY(newPosition);
relativeView.requestLayout();
}
}
#Override
public void onPause() {
super.onPause();
keyboardHeightProvider.setKeyboardHeightObserver(null);
}
#Override
public void onResume() {
super.onResume();
keyboardHeightProvider.setKeyboardHeightObserver(this);
}
#Override
public void onDestroy() {
super.onDestroy();
keyboardHeightProvider.close();
}
}
activity_main.xml :
<?xml version="1.0" encoding="utf-8"?>
<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:id="#+id/activitylayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/imageView2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
/>
<RelativeLayout
android:id="#+id/bottomEditor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
>
<EditText
android:id="#+id/edit_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:layout_toStartOf="#+id/btn_send"
android:hint="Add caption"
android:paddingBottom="12dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingStart="8dp"
android:paddingTop="12dp"
/>
<ImageButton
android:id="#+id/btn_send"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignBottom="#+id/edit_message"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
app:srcCompat="#android:drawable/ic_menu_send"
/>
</RelativeLayout>
</RelativeLayout>
P.S. : Keyboard height calculation code is copied from siebeprojects
Here is demo example app of implementation.
final View activityRootView = findViewById(R.id.mainScroll);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int heightView = activityRootView.getHeight();
int widthView = activityRootView.getWidth();
if (1.0 * widthView / heightView > 1) {
Log.d("keyboarddddd visible", "no");
relativeLayoutForImage.setVisibility(View.GONE);
relativeLayoutStatic.setVisibility(View.GONE);
//Make changes for Keyboard not visible
} else {
Log.d("keyboarddddd visible ", "yes");
relativeLayoutForImage.setVisibility(View.VISIBLE);
relativeLayoutStatic.setVisibility(View.VISIBLE);
//Make changes for keyboard visible
}
}
});
For me i did not want to assume that keyboards heights are a certain measurement. Whatever view your concerned about make a onTouchListener and then do this:
setOnTouchListener(new OnTouchListener() {
Runnable shifter=new Runnable(){
public void run(){
try {
int[] loc = new int[2];
//get the location of someview which gets stored in loc array
findViewById(R.id.someview).getLocationInWindow(loc);
//shift so user can see someview
myscrollView.scrollTo(loc[0], loc[1]);
}
catch (Exception e) {
e.printStackTrace();
}
}}
};
Rect scrollBounds = new Rect();
View divider=findViewById(R.id.someview);
myscollView.getHitRect(scrollBounds);
if (!divider.getLocalVisibleRect(scrollBounds)) {
// the divider view is NOT within the visible scroll window thus we need to scroll a bit.
myscollView.postDelayed(shifter, 500);
}
});
//essentially we make a runnable that scrolls to a new location of some view that you WANT visible on the screen. you execute that runnable only if its not within the scrollviews bounds (its not on the screen). This way it shifts the scrollview to the referenced view (in my case 'someview' which was a line divider).
In my opinion the easiest way to do this it is this combination of the two changes:
android:windowSoftInputMode="adjustResize"
in your AndroidManifest.xml
+
getWindow().setBackgroundDrawable(your_image_drawable);
in your activity in #onCreate() method
It works for me.
The best solution is to use a DialogFragment
Show dialog
DialogFragment.show(getSupportFragmentManager(), DialogFragment.TAG);
Full screen
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = new Dialog(getActivity(), R.style.MainDialog) { //set the style, the best code here or with me, we do not change
#Override
public void onBackPressed() {
super.onBackPressed();
getActivity().finish();
}
};
return dialog;
}
Style
<style name="MainDialog" parent="#android:style/Theme.Dialog">
<item name="android:windowBackground">#android:color/transparent</item>
<item name="android:windowFrame">#null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">false</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowContentOverlay">#null</item>
<item name="android:background">#null</item>
<item name="android:windowAnimationStyle">#null</item>
</style>
Layout Activity
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/black">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Layout dialog fragment
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/transparent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:background="#color/background_transparent_60"
android:gravity="center_vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="#dimen/spacing_1_8dp"
android:layout_marginLeft="#dimen/spacing_1_8dp"
android:layout_marginRight="#dimen/spacing_1_8dp"
android:layout_weight="1"
android:hint="#string/comment_entry_hint"
android:inputType="textMultiLine"
android:maxLines="4"
android:textColor="#color/white"
android:textColorHint="#color/secondary_text_hint"
android:textSize="#dimen/text_2_12sp" />
<ImageView
android:layout_width="#dimen/livestream_comment_height"
android:layout_height="#dimen/livestream_comment_height"
android:layout_margin="#dimen/spacing_1_8dp"
android:src="#drawable/ic_send" />
</LinearLayout>
</RelativeLayout>
The solution that worked for me was in AndroidManifest.xml in that activity tag just put
android:windowSoftInputMode="stateHidden|adjustResize|adjustNothing"
All set..Hope this will work for you.
final View activityRootView = findViewById(R.id.mainScroll);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int heightView = activityRootView.getHeight();
int widthView = activityRootView.getWidth();
if (1.0 * widthView / heightView > 1) {
Log.d("keyboarddddd visible", "no");
relativeLayoutForImage.setVisibility(View.GONE);
relativeLayoutStatic.setVisibility(View.GONE);
//Make changes for Keyboard not visible
//relativeLayoutForImage.setVisibility(View.VISIBLE);
//relativeLayoutStatic.setVisibility(View.VISIBLE);
} else {
Log.d("keyboarddddd visible ", "yes");
relativeLayoutForImage.setVisibility(View.VISIBLE);
relativeLayoutStatic.setVisibility(View.VISIBLE);
//Make changes for keyboard visible
// relativeLayoutForImage.setVisibility(View.GONE);
//relativeLayoutStatic.setVisibility(View.GONE);
}
}
});
In the application "Google Play", we can see that if you click on any item (LinearLayout, button, imageview, textview ...) this has on it a blue coat.
Being above is not a simple background with android: state_pressed = "true" android: state_focused = "true" ...
Any idea how to apply that effect?
For example, I have a LinearLayout with multiple images and text.
This LinearLayout acts as a "button", my intention is that pressing change in appearance having a blue layer on top, as does Google.
I typically use custom background but that applies back and not over.
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:state_focused="true" android:drawable="#drawable/focus_blue_default" />
</selector>
And focus_blue_default.xml:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#8051C9EC" />
</shape>
</item>
</layer-list>
This is only for reference and I do not think it is practical and it is not tested and ...
Since the answer is accepted, I will state the problems of this code (the ones I can think of right now):
As #user2558882 stated in his comment on the answer, if a subclass uses setContentView(view, params) the params are lost.
This will not work with inflated views -i.e. adapters; Yet, you can solve by calling fix(View) on any inflated view.
It only works with colors. This can be fixed as well.
I think this might work with sherlock-actionbar.
The code has not been tested so please test it.
It shifts your whole view tree one more level by adding a RelativeLayout. This can be fixed using addContentView. I did use this RelativeLayout because I answered this fast and I also think the current code will work better on all phones and apis. This is true because the whole solution is compacted in a RelativeLayout whose behavior is pretty consistent. After all I am only using RelativeLayout.LayoutParams which is the very basic functionality of this ViewGroup.
Solution
Create a BaseActivity.java:
package mobi.sherif.overlay;
import android.app.Activity;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.RelativeLayout;
public class BaseActivity extends Activity {
RelativeLayout mRl;
View mImage;
View mContent;
#Override
public void setContentView(int layoutResID) {
setContentView(LayoutInflater.from(this).inflate(layoutResID, null));
}
#Override
public void setContentView(View view) {
setContentView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
}
#Override
public void setContentView(View view, LayoutParams params) {
mContent = view;
fix(view);
push(view);
}
protected void refix() {
fix(mContent);
}
private void push(View view) {
mRl = new RelativeLayout(this);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
mRl.addView(view, params);
mImage = new View(this);
mImage.setVisibility(View.GONE);
params = new RelativeLayout.LayoutParams(0, 0);
mRl.addView(mImage, params);
super.setContentView(mRl);
}
protected void fix(View child) {
if (child == null)
return;
doFix(child);
if (child instanceof ViewGroup) {
fix((ViewGroup) child);
}
}
private void fix(ViewGroup parent) {
for (int i = 0; i < parent.getChildCount(); i++) {
fix(parent.getChildAt(i));
}
}
private void doFix(View child) {
if(child.getTag()!=null && child.getTag().getClass() == String.class) {
String color = (String) child.getTag();
int theColor;
try {
theColor = Color.parseColor(color);
child.setTag(theColor);
} catch (Exception e) {
theColor = -1;
}
if(theColor != -1) {
child.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mImage.setBackgroundColor((Integer) v.getTag());
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mImage.getLayoutParams();
params.leftMargin = getLeftWithRespectTo(v, mRl);
params.topMargin = getTopWithRespectTo(v, mRl);
params.width = v.getWidth();
params.height = v.getHeight();
mImage.setVisibility(View.VISIBLE);
mImage.requestLayout();
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mImage.setVisibility(View.GONE);
default:
break;
}
return v.onTouchEvent(event);
}
});
}
}
}
private int getLeftWithRespectTo(View view, View relative) {
int left = 0;
View temp = view;
do {
left += temp.getLeft();
temp = (View) temp.getParent();
}
while(temp!=relative);
return left;
}
private int getTopWithRespectTo(View view, View relative) {
int top = 0;
View temp = view;
do {
top += temp.getTop();
temp = (View) temp.getParent();
}
while(temp!=relative);
return top;
}
}
Now extend this BaseActivity and any view you want to overlay on it a color use android:tag="#AARRGGBB" like this:
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="#331111ff"
android:text="#string/hello_world" />
TEST: If you use the xml up in activity_main.xml like this:
<RelativeLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="17dp"
android:layout_marginTop="90dp"
android:tag="#331111ff"
android:text="TextView" />
</RelativeLayout>
And use this as your MainActivity.java:
package mobi.sherif.overlay;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;
public class MainActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
findViewById(R.id.textView1).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "text tpicjed", Toast.LENGTH_SHORT).show();
}
});
}
}
Lets say you want to achieve this effect on a Button:
Create a class which extends Android Button class
Have a boolean flag as a class member which will be set to true when you want this effect to appear (I would say that you can set it's value according to the touch events detected in onTouchEvent(MotionEvent event) - just override it in your class and analyse those events).
Override onDraw() method, and after the call for the super, add an if clause which will paint this colored rect over the whole view (using Paint) if the boolean flag is true.
Few important points:
Alternative to step 3: You can achieve the same effect by having a (semi transparent, blue) drawable which you will add or remove according to the flag.
You may need to invalidate your view after changing the boolean value.
While this may not be exactly what you're asking for, I did come up with an easily reusable way to make controls semi-transparent when pressing on them, then have them return to their normal display when you stop pressing on them.
Here's the code:
public class ViewTouchHighlighter implements OnTouchListener
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
AlphaAnimation animation = new AlphaAnimation(1, 0.5f);
animation.setDuration(0);
v.startAnimation(animation);
}
else
{
AlphaAnimation animation = new AlphaAnimation(0.5f, 1);
animation.setDuration(0);
v.startAnimation(animation);
}
return false;
}
}
Then, you can simply grab any view, and give it this behavior like so:
view.setOnTouchListener(new ViewTouchHighlighter());
I realize this is a bit off from what you're looking for, but if highlighting is all you want, this could serve as a nice solution.
If you're insisting on the blue effect, and/or you want something that appears blue when selected, not when simply touched, then I'm afraid I'm going to agree with #Daniel & suggest that you inherit from any component you might want to do this with and add your custom code there.
From what I can tell, the container layout (for individual items) being used on Google play is a RelativeLayout. And achieving this effect is fairly simple using a RelativeLayout. You already have most of the components ready. Here's the final step:
Layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white" >
<ImageView
........../>
<TextView
........../>
<WhateverView
........../>
<!-- The final piece -->
<View
android:id="#+id/clickableView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:background="#drawable/state_selector_drawable" />
</RelativeLayout>
And, in your activity, you will be adding an OnClickListener to this View:
View clickableView = findViewById(R.id.clickableView);
clickableView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Toast.makeText(MainActivity.this,
"Click registered.", Toast.LENGTH_SHORT).show();
}
});
Let's say you already have a LinearLayout set up and do not wish to switch over to RelativeLayout. That's fine. Place the LinearLayout along with its children inside a RelativeLayout. And add the View defined above:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white" >
<ImageView
........../>
<TextView
........../>
<WhateverView
........../>
</LinearLayout>
<!-- The final piece -->
<View
android:id="#+id/clickableView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:background="#drawable/state_selector_drawable" />
</RelativeLayout>
I really want to implement this (the side navigation) in an app of my own, does anyone know how Google managed to do this?
They seem to have pulled the current window aside and put in a fly-in navigation of their own.
In fact, there's a way to do this. Even without implementing your own ActionBar.
Just have a look at the hierachyviewer! (Located in the tools directory)
There's the DecorView, and a LinearLayout as a child. This LinearLayout contains both the ActionBar and the other content. So, you can simply apply some FrameLayout.LayoutParams to this LinearLayout and get some space on the left side this way. Then, you can fill this space with your menu-ListView and overlay the other content with a FrameLayout, that, when it's clicked, collapses the menu. So, here's some code:
First, the class for collapsing / expanding (SlideMenu.java):
package your.cool.app;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.view.animation.TranslateAnimation;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
public class SlideMenu {
//just a simple adapter
public static class SlideMenuAdapter extends ArrayAdapter<SlideMenu.SlideMenuAdapter.MenuDesc> {
Activity act;
SlideMenu.SlideMenuAdapter.MenuDesc[] items;
class MenuItem {
public TextView label;
public ImageView icon;
}
static class MenuDesc {
public int icon;
public String label;
}
public SlideMenuAdapter(Activity act, SlideMenu.SlideMenuAdapter.MenuDesc[] items) {
super(act, R.id.menu_label, items);
this.act = act;
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View rowView = convertView;
if (rowView == null) {
LayoutInflater inflater = act.getLayoutInflater();
rowView = inflater.inflate(R.layout.menu_listitem, null);
MenuItem viewHolder = new MenuItem();
viewHolder.label = (TextView) rowView.findViewById(R.id.menu_label);
viewHolder.icon = (ImageView) rowView.findViewById(R.id.menu_icon);
rowView.setTag(viewHolder);
}
MenuItem holder = (MenuItem) rowView.getTag();
String s = items[position].label;
holder.label.setText(s);
holder.icon.setImageResource(items[position].icon);
return rowView;
}
}
private static boolean menuShown = false;
private static View menu;
private static LinearLayout content;
private static FrameLayout parent;
private static int menuSize;
private static int statusHeight = 0;
private Activity act;
SlideMenu(Activity act) {
this.act = act;
}
//call this in your onCreate() for screen rotation
public void checkEnabled() {
if(menuShown)
this.show(false);
}
public void show() {
//get the height of the status bar
if(statusHeight == 0) {
Rect rectgle = new Rect();
Window window = act.getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
statusHeight = rectgle.top;
}
this.show(true);
}
public void show(boolean animate) {
menuSize = Functions.dpToPx(250, act);
content = ((LinearLayout) act.findViewById(android.R.id.content).getParent());
FrameLayout.LayoutParams parm = (FrameLayout.LayoutParams) content.getLayoutParams();
parm.setMargins(menuSize, 0, -menuSize, 0);
content.setLayoutParams(parm);
//animation for smooth slide-out
TranslateAnimation ta = new TranslateAnimation(-menuSize, 0, 0, 0);
ta.setDuration(500);
if(animate)
content.startAnimation(ta);
parent = (FrameLayout) content.getParent();
LayoutInflater inflater = (LayoutInflater) act.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
menu = inflater.inflate(R.layout.menu, null);
FrameLayout.LayoutParams lays = new FrameLayout.LayoutParams(-1, -1, 3);
lays.setMargins(0,statusHeight, 0, 0);
menu.setLayoutParams(lays);
parent.addView(menu);
ListView list = (ListView) act.findViewById(R.id.menu_listview);
list.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//handle your menu-click
}
});
if(animate)
menu.startAnimation(ta);
menu.findViewById(R.id.overlay).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
SlideMenu.this.hide();
}
});
Functions.enableDisableViewGroup((LinearLayout) parent.findViewById(android.R.id.content).getParent(), false);
((ExtendedViewPager) act.findViewById(R.id.viewpager)).setPagingEnabled(false);
((ExtendedPagerTabStrip) act.findViewById(R.id.viewpager_tabs)).setNavEnabled(false);
menuShown = true;
this.fill();
}
public void fill() {
ListView list = (ListView) act.findViewById(R.id.menu_listview);
SlideMenuAdapter.MenuDesc[] items = new SlideMenuAdapter.MenuDesc[5];
//fill the menu-items here
SlideMenuAdapter adap = new SlideMenuAdapter(act, items);
list.setAdapter(adap);
}
public void hide() {
TranslateAnimation ta = new TranslateAnimation(0, -menuSize, 0, 0);
ta.setDuration(500);
menu.startAnimation(ta);
parent.removeView(menu);
TranslateAnimation tra = new TranslateAnimation(menuSize, 0, 0, 0);
tra.setDuration(500);
content.startAnimation(tra);
FrameLayout.LayoutParams parm = (FrameLayout.LayoutParams) content.getLayoutParams();
parm.setMargins(0, 0, 0, 0);
content.setLayoutParams(parm);
Functions.enableDisableViewGroup((LinearLayout) parent.findViewById(android.R.id.content).getParent(), true);
((ExtendedViewPager) act.findViewById(R.id.viewpager)).setPagingEnabled(true);
((ExtendedPagerTabStrip) act.findViewById(R.id.viewpager_tabs)).setNavEnabled(true);
menuShown = false;
}
}
Some helping methods (for me, in static Functions.java):
public static int dpToPx(int dp, Context ctx) {
Resources r = ctx.getResources();
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
}
//originally: http://stackoverflow.com/questions/5418510/disable-the-touch-events-for-all-the-views
//modified for the needs here
public static void enableDisableViewGroup(ViewGroup viewGroup, boolean enabled) {
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; i++) {
View view = viewGroup.getChildAt(i);
if(view.isFocusable())
view.setEnabled(enabled);
if (view instanceof ViewGroup) {
enableDisableViewGroup((ViewGroup) view, enabled);
} else if (view instanceof ListView) {
if(view.isFocusable())
view.setEnabled(enabled);
ListView listView = (ListView) view;
int listChildCount = listView.getChildCount();
for (int j = 0; j < listChildCount; j++) {
if(view.isFocusable())
listView.getChildAt(j).setEnabled(false);
}
}
}
}
Then, the layouts:
Layout of the menu (res/layout/menu.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:orientation="vertical"
android:layout_height="fill_parent"
android:layout_width="250dip"
android:background="#color/darkblack">
<ListView
android:id="#+id/menu_listview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:divider="#color/dividerblack"
android:dividerHeight="2dip" />
</LinearLayout>
<FrameLayout
android:id="#+id/overlay"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
</LinearLayout>
Layout of the listitems (res/layout/menu_listitem.xml):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="fill_parent" >
<ImageView
android:id="#+id/menu_icon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="5dip"
android:layout_marginLeft="10dip"
android:layout_marginTop="10dip"
android:layout_marginBottom="10dip" />
<TextView
android:id="#+id/menu_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#color/white"
android:textSize="24dp"
android:layout_marginTop="10dip"
android:layout_marginBottom="10dip" />
</LinearLayout>
How to use it:
In your onCreate():
private SlideMenu slidemenu;
#Override
public void onCreate(Bundle savedInstanceState) {
//your onCreate code
slidemenu = new SlideMenu(this);
slidemenu.checkEnabled();
}
In the handler for your ActionBar homebutton:
slidemenu.show();
That's it!
And now, a little screenshot of it in action:
As far as I know, it is working. If you experience any problems or my explanations are not clear, please contact me!
EDIT: ExtendedViewPager & ExtendedPagerStrip:
ExtendedViewPager:
package your.cool.app;
//source: http://blog.svpino.com/2011/08/disabling-pagingswiping-on-android.html
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
public class ExtendedViewPager extends ViewPager {
private boolean enabled;
public ExtendedViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
this.enabled = true;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (this.enabled) {
return super.onTouchEvent(event);
}
return false;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (this.enabled) {
return super.onInterceptTouchEvent(event);
}
return false;
}
public void setPagingEnabled(boolean enabled) {
this.enabled = enabled;
}
}
ExtendedPagerTabStrip:
package your.cool.app;
//source: http://blog.svpino.com/2011/08/disabling-pagingswiping-on-android.html
import android.content.Context;
import android.support.v4.view.PagerTabStrip;
import android.util.AttributeSet;
import android.view.MotionEvent;
public class ExtendedPagerTabStrip extends PagerTabStrip {
private boolean enabled;
public ExtendedPagerTabStrip(Context context, AttributeSet attrs) {
super(context, attrs);
this.enabled = true;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (this.enabled) {
return super.onTouchEvent(event);
}
return false;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (this.enabled) {
return super.onInterceptTouchEvent(event);
}
return false;
}
public void setNavEnabled(boolean enabled) {
this.enabled = enabled;
}
}
I use this SlideMenu for an Activity with a ViewPager with PagerTabStrip for tabs like Talk, Market etc. You can't disable these Views in an easy way, so the two classes above just extend them to stop the onTouch event when disabled.
There are several attempts at doing this, however I have yet to find a lib or source code on how to implement it successfully with actionbar accross all api levels. One promising lib is here
https://github.com/jfeinstein10/SlidingMenu
here is a video of the example app.
here is the Google Play app link.
This does work with ActionbarSherlock. You will have to build the SlidingMenu library with ABS to get it working. Works and looks great!
Did a roundup of the original implementation and added XML parsing as well as autodetection of a possibly present actionbar, so it works with the native as well as a support action bar such as ActionBarSherlock.
The whole thing is now a library project together with an example app and is described over at Sliding Menu for android Thanks to scirocco for the initial idea and code!
If you are using API level greater that 11 you can use a much simpler approach inspired by the answer given by #Scirocco
// get content parent that is basically the whole
// app screen (viewed from hierarchy viewer)
final LinearLayout content =
(LinearLayout) findViewById(android.R.id.content).getParent();
// make new value animator with range from 0 to 1
final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
// set custom duration
animator.setDuration(500);
// on update is called for every value in the
// given range in time frame defined by the duration
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
// get the current value
float value = ((Float) (animation.getAnimatedValue())).floatValue();
// translate by that value, minus means translate left
content.setTranslationX(-250 * value);
}
});
// start the animator
animator.start();
// make or inflate custom view for test purposes
Button textView = new Button(this);
textView.setText("TestButton");
// add it to the frame layout that is the parent of the content on position 0
FrameLayout parent = (FrameLayout) content.getParent();
parent.addView(textView, 0);
The idea here is to use ValueAnimator that transforms and not just animates the main layout with the Action bar, so you can interact with the inflated view you want to use as a sliding panel.
You should replace the hardcoded values with something that is of use to your app.
I hope this helps :)
Well currently im working on a project and came across Sliding menu,i googled but gets very disappointed to see that no one has given some piece of code or some hint for how to start making a sliding menu,but every one has given link to some github's projects/libraries to use,I decided to do it myself and finally i have my own Sliding Menu Ready...
I have Spent two days on it
1. on making animations of sliding
2. on making it work with all screen resolutions
Its really easy and simple once you get some idea about Animations, i have read some where,its not sensible to re-invent the Wheel(people who are refering to github source code of sliding menu),but i beleif that you should atleast once try to make your own so you get a idea how it actually works and functions :P
so this is a picture of how my sliding menu will going to work
1.Find.xml //later in the code it will be refer as findLayout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<RelativeLayout
android:id="#+id/find_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="#+id/header"
android:layout_width="match_parent"
android:layout_height="60dp"
android:padding="2dp"
android:background="#drawable/main_header">
<Button
android:id="#+id/filter"
android:layout_width="40dp"
android:layout_height="30dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:background="#drawable/filter_button" />
<TextView
android:id="#+id/city"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/filter"
android:layout_marginLeft="20dp"
android:layout_marginTop="3dp"
android:text="Islamabad"
android:textSize="22sp"
android:textStyle="bold"
android:textColor="#android:color/primary_text_dark"/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/city"
android:layout_alignLeft="#+id/city">
<TextView
android:id="#+id/interested_in"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="Men and Women"
android:textSize="12sp"
android:textColor="#android:color/primary_text_dark"/>
<ImageView
android:id="#+id/separator"
android:layout_width="2dp"
android:layout_height="18dp"
android:layout_toRightOf="#+id/interested_in"
android:layout_marginLeft="4dp"
android:src="#drawable/separator_1"
android:layout_centerVertical="true" />
<TextView
android:id="#+id/age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_toRightOf="#+id/separator"
android:layout_centerVertical="true"
android:text="18-24 years"
android:textSize="12sp"
android:textColor="#android:color/primary_text_dark"/>
<ImageView
android:id="#+id/separator_1"
android:layout_width="2dp"
android:layout_height="18dp"
android:layout_toRightOf="#+id/age"
android:layout_marginLeft="4dp"
android:src="#drawable/separator_1"
android:layout_centerVertical="true" />
<TextView
android:id="#+id/distance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_toRightOf="#+id/separator_1"
android:layout_centerVertical="true"
android:text=">30km"
android:textSize="12sp"
android:textColor="#android:color/primary_text_dark" />
</RelativeLayout>
</RelativeLayout>
<GridView
android:id="#+id/users_grid"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/header"
android:numColumns="4">
</GridView>
</RelativeLayout>
<include
layout="#layout/filter"/> //here i included the filter.xml, which is on top of find.xml layout and is initially invisible
</RelativeLayout>
2.Filter.xml //later in code refer as FilterLayout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/filter_layout"
android:visibility="invisible"
android:layout_width="260dp"
android:layout_height="match_parent"
android:background="#drawable/grey_bg" >
<ImageView
android:id="#+id/profile_pic"
android:layout_width="match_parent"
android:layout_height="220dp"
android:src="#drawable/pic"/>
<RelativeLayout
android:id="#+id/header"
android:layout_width="match_parent"
android:layout_height="55dp"
android:paddingLeft="10dp"
android:paddingTop="5dp"
android:layout_below="#+id/profile_pic"
android:background="#drawable/light_blue_header">
<TextView
android:id="#+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="Raja Babar"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#android:color/primary_text_dark"/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/name"
android:layout_alignLeft="#+id/name">
<TextView
android:id="#+id/gender"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="Male"
android:textSize="12sp"
android:textColor="#android:color/primary_text_dark" />
<ImageView
android:id="#+id/seperator"
android:layout_width="2dp"
android:layout_height="20dp"
android:layout_toRightOf="#+id/gender"
android:layout_marginLeft="5dp"
android:src="#drawable/separator_1"
android:layout_centerVertical="true" />
<TextView
android:id="#+id/age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/seperator"
android:layout_marginLeft="5dp"
android:layout_centerVertical="true"
android:text="22 years"
android:textSize="12sp"
android:textColor="#android:color/primary_text_dark" />
</RelativeLayout>
</RelativeLayout>
<ScrollView
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_below="#+id/header"
android:layout_marginTop="15dp"
android:layout_centerHorizontal="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/filter_options"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/filter_options"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#android:color/primary_text_light"/>
<RelativeLayout
android:id="#+id/interested_in_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="15dp"
android:paddingRight="40dp"
android:layout_below="#+id/filter_options"
android:background="#drawable/interested_in_field">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:text="#string/gender"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#android:color/primary_text_light"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text="#string/women_men"
android:textSize="18sp"
android:textColor="#33b9cd" />
</RelativeLayout>
<RelativeLayout
android:id="#+id/age_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="15dp"
android:paddingRight="40dp"
android:layout_below="#+id/interested_in_layout"
android:background="#drawable/age_field_1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:text="#string/age"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#android:color/primary_text_light"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text="18-24 years"
android:textSize="18sp"
android:textColor="#33b9cd"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="15dp"
android:paddingRight="40dp"
android:layout_below="#+id/age_layout"
android:background="#drawable/distance_field">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:text="#string/distance"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#android:color/primary_text_light"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text=">30km"
android:textSize="18sp"
android:textColor="#33b9cd"/>
</RelativeLayout>
</RelativeLayout>
</ScrollView>
</RelativeLayout>
In find.xml i have included filter.xml initially which is invisible
Now FilterAnimation.java
package matchat.helpers;
import com.s3.matchat.R;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.widget.RelativeLayout;
public class FilterAnimation implements AnimationListener
{
Context context;
RelativeLayout filterLayout, otherLayout;
private Animation filterSlideIn, filterSlideOut, otherSlideIn, otherSlideOut;
private static int otherLayoutWidth, otherLayoutHeight;
private boolean isOtherSlideOut = false;
private int deviceWidth;
private int margin;
public FilterAnimation(Context context)
{
this.context = context;
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
deviceWidth = displayMetrics.widthPixels; // as my animation is x-axis related so i gets the device width and will use that width,so that this sliding menu will work fine in all screen resolutions
}
public void initializeFilterAnimations(RelativeLayout filterLayout)
{
this.filterLayout = filterLayout;
filterSlideIn = AnimationUtils.loadAnimation(context, R.anim.filter_slide_in);
filterSlideOut = AnimationUtils.loadAnimation(context, R.anim.filter_slide_out);
}
public void initializeOtherAnimations(RelativeLayout otherLayout)
{
this.otherLayout = otherLayout;
otherLayoutWidth = otherLayout.getWidth();
otherLayoutHeight = otherLayout.getHeight();
otherSlideIn = AnimationUtils.loadAnimation(context, R.anim.other_slide_in);
otherSlideIn.setAnimationListener(this);
otherSlideOut = AnimationUtils.loadAnimation(context, R.anim.other_slide_out);
otherSlideOut.setAnimationListener(this);
}
public void toggleSliding()
{
if(isOtherSlideOut) //check if findLayout is already slided out so get so animate it back to initial position
{
filterLayout.startAnimation(filterSlideOut);
filterLayout.setVisibility(View.INVISIBLE);
otherLayout.startAnimation(otherSlideIn);
}
else //slide findLayout Out and filterLayout In
{
otherLayout.startAnimation(otherSlideOut);
filterLayout.setVisibility(View.VISIBLE);
filterLayout.startAnimation(filterSlideIn);
}
}
#Override
public void onAnimationEnd(Animation animation)
{
if(isOtherSlideOut) //Now here we will actually move our view to the new position,because animations just move the pixels not the view
{
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(otherLayoutWidth, otherLayoutHeight);
otherLayout.setLayoutParams(params);
isOtherSlideOut = false;
}
else
{
margin = (deviceWidth * 80) / 100; //here im coverting device percentage width into pixels, in my other_slide_in.xml or other_slide_out.xml you can see that i have set the android:toXDelta="80%",so it means the layout will move to 80% of the device screen,to work across all screens i have converted percentage width into pixels and then used it
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(otherLayoutWidth, otherLayoutHeight);
params.leftMargin = margin;
params.rightMargin = -margin; //same margin from right side (negavite) so that our layout won't get shrink
otherLayout.setLayoutParams(params);
isOtherSlideOut = true;
dimOtherLayout();
}
}
#Override
public void onAnimationRepeat(Animation animation)
{
}
#Override
public void onAnimationStart(Animation animation)
{
}
private void dimOtherLayout()
{
AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.5f);
alphaAnimation.setFillAfter(true);
otherLayout.startAnimation(alphaAnimation);
}
}
Now Find.java
package main.matchat.activities;
import matchat.helpers.FilterAnimation;
import com.s3.matchat.R;
import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.View.OnClickListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.Button;
import android.widget.RelativeLayout;
public class Find extends Activity implements OnClickListener
{
RelativeLayout filterLayout, findLayout;
Button btFilter;
FilterAnimation filterAnimation;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.find);
filterLayout = (RelativeLayout)findViewById(R.id.filter_layout);
findLayout = (RelativeLayout)findViewById(R.id.find_layout);
btFilter = (Button)findViewById(R.id.filter);
btFilter.setOnClickListener(this);
filterAnimation = new FilterAnimation(this);
initializeAnimations();
}
private void initializeAnimations()
{ //Setting GlobolLayoutListener,when layout is completely set this function will get called and we can have our layout onbject with correct width & height,else if you simply try to get width/height of your layout in onCreate it will return 0
final ViewTreeObserver filterObserver = filterLayout.getViewTreeObserver();
filterObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
filterLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
int deviceWidth = displayMetrics.widthPixels;
int filterLayoutWidth = (deviceWidth * 80) / 100; //here im coverting device percentage width into pixels, in my other_slide_in.xml or other_slide_out.xml you can see that i have set the android:toXDelta="80%",so it means the layout will move to 80% of the device screen,to work across all screens i have converted percentage width into pixels and then used it
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(filterLayoutWidth, RelativeLayout.LayoutParams.MATCH_PARENT);
filterLayout.setLayoutParams(params);//here im setting the layout params for my filter.xml because its has width 260 dp,so work it across all screen i first make layout adjustments so that it work across all screens resolution
filterAnimation.initializeFilterAnimations(filterLayout);
}
});
final ViewTreeObserver findObserver = findLayout.getViewTreeObserver();
findObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
findLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
filterAnimation.initializeOtherAnimations(findLayout);
}
});
}
#Override
public void onClick(View v)
{
int id = v.getId();
switch(id)
{
case R.id.filter:
filterAnimation.toggleSliding();
break;
}
}
}
Here are the animations res/anim
1.filter_slide_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/decelerate_interpolator">
<translate
android:fromXDelta="-100%"
android:toXDelta="0%"
android:duration="1000"
android:fillEnabled="true" />
</set>
2.filter_slide_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/decelerate_interpolator">
<translate
android:fromXDelta="0%"
android:toXDelta="-100%"
android:duration="1000"/>
</set>
3.other_slide_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/decelerate_interpolator" >
<translate
android:fromXDelta="0%"
android:toXDelta="-80%"
android:duration="1000"
android:fillEnabled="true"/>
</set>
4.other_slide_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/decelerate_interpolator">
<translate
android:fromXDelta="0%"
android:toXDelta="80%"
android:duration="1000"
android:fillEnabled="true"/>
</set>
There you go a complete working and functional Sliding Menu, and you can customized it to meet your requirements,if any one still have some problems setting up,feel free to ask,i feel pleasure to help you out :)
I've created my own solution for sliding away the view and revealing a menu underneath, as many other solutions appeared to not work on older Android versions or lacked proper instructions on how to get it to work.
My solution has the following features:
Provides support for sliding away a view to reveal a menu that lies underneath it
Both the menu and the view above can be any custom View
Supported on old Android versions (tested to work at least on Android 2.2)
Works with PhoneGap / Cordova projects
The solution uses a custom layout, called SlidingMenuLayout, that you are expected to add 2 views to. The first view you add is the menu, the second is the main view.
The simplest way to add the layout to your existing project is to override your Activity's setContentView() method:
#Override
public void setContentView(View view) {
SlidingMenuLayout layout = new SlidingMenuLayout(this);
layout.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
0.0F));
layout.addView(new MenuView(this));
layout.addView(view);
super.setContentView(layout);
}
In this example, MenuView is the view that will actually show the menu. It is up to you to implement this view.
Finally, you can add a button (typically in the top left corner of your main view), that calls openMenu() or closeMenu() on the layout as appropriate.
The code for SlidingMenuLayout is found on the GitHub project page.
For those of you who uses the SlidingMenu library (https://github.com/jfeinstein10/SlidingMenu) there is a way to jack it in and it seems to work! With help of #Scirocco put this in your onCreate for the activity:
ViewGroup decorView = (ViewGroup) getWindow().getDecorView();
mSlidingMenu = new SlidingMenu(this);
ViewGroup mainContent = (ViewGroup) decorView.getChildAt(0);
decorView.removeView(mainContent);
mSlidingMenu.setContent(mainContent);
decorView.addView(mSlidingMenu);
mMenu = (LinearLayout) View.inflate(this, R.layout.menuview, null);
mSlidingMenu.setMenu(mMenu);
mSlidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN);
mSlidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset);
basically what it does is replacing the linearlayout in decor view with the slidingmenu instead.
Notice: Ive only tested it lightly but it seems to work.
public class ImprovedSlidingPaneLayout extends SlidingPaneLayout {
Context context;
FrameLayout left;
FrameLayout right;
Boolean canOpen = true;
public ImprovedSlidingPaneLayout(Context context) {
super(context);
this.context = context;
this.left = new FrameLayout(context);
this.right = new FrameLayout(context);
this.addView(left);
this.addView(right);
}
public ImprovedSlidingPaneLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (canOpen)
return super.onInterceptTouchEvent(ev);
else
return false;
}
public ImprovedSlidingPaneLayout canOpen(Boolean canOpen) {
this.canOpen = canOpen;
return this;
}
public ImprovedSlidingPaneLayout makeActionBarSlide(Window window){
ViewGroup decorView = (ViewGroup) window.getDecorView();
ViewGroup mainContent = (ViewGroup) decorView.getChildAt(0);
decorView.removeView(mainContent);
setContentView(mainContent);
decorView.addView(this);
return this;
}
public ImprovedSlidingPaneLayout setMenuView(View view){
if((left.getChildCount()== 1)){
left.removeView(left.getChildAt(0));
}
left.addView(view);
return this;
}
public ImprovedSlidingPaneLayout setContentView(View view){
if((right.getChildCount()== 1)){
right.removeView(right.getChildAt(0));
}
right.addView(view);
return this;
}
public ImprovedSlidingPaneLayout setMenuWidth(int width){
left.setLayoutParams(new SlidingPaneLayout.LayoutParams(width, ViewGroup.LayoutParams.MATCH_PARENT));
return this;
}
}
this is my class extends SlidingPaneLayout. Can slide with actio