StackOverflowError when keybord is dismissed - java

I have an custom view that is extended from MultiAutoCompleteTextView to create chiped view like contacts in gmail. when i a contact to this view and the keyborad is dismissed it casuse stack overflow. it happens only in my nexus 4 this is the logcat.
java.lang.StackOverflowError
at android.text.DynamicLayout.reflow(DynamicLayout.java:284)
at android.text.DynamicLayout.<init>(DynamicLayout.java:170)
at android.widget.TextView.makeSingleLayout(TextView.java:6134)
at android.widget.TextView.makeNewLayout(TextView.java:6032)
at android.widget.TextView.checkForRelayout(TextView.java:6571)
at android.widget.TextView.onRtlPropertiesChanged(TextView.java:8672)
at android.view.View.resolvePadding(View.java:12407)
at android.view.View.getPaddingLeft(View.java:15603)
at com.tokenautocomplete.TokenCompleteTextView.maxTextWidth(TokenCompleteTextView.java:260)
at com.tokenautocomplete.TokenCompleteTextView.access$1000(TokenCompleteTextView.java:54)
at com.tokenautocomplete.TokenCompleteTextView$ViewSpan.prepView(TokenCompleteTextView.java:822)
at com.tokenautocomplete.TokenCompleteTextView$ViewSpan.getSize(TokenCompleteTextView.java:841)
at com.tokenautocomplete.TokenCompleteTextView$TokenImageSpan.getSize(TokenCompleteTextView.java:885)
this is my prep view code
private void prepView() {
int widthSpec = MeasureSpec.makeMeasureSpec((int)maxTextWidth(), MeasureSpec.AT_MOST);
int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
view.measure(widthSpec, heightSpec);
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
}
this is line 260 of tokenCompleteTextView
private float maxTextWidth() {
return getWidth() - getPaddingLeft() - getPaddingRight();
}
is use TokenAutoComplete libray for the token view.
this is my layout
<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/gray"
android:clickable="true">
<LinearLayout
android:id="#+id/llsearch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:orientation="horizontal"
android:weightSum="4"
android:background="#color/listview_color"
android:layout_marginTop="#dimen/hdpi_4dp"
android:layout_marginBottom="#dimen/hdpi_4dp"
android:gravity="center_vertical">
<in.ispg.chipview.ConatctCompleteTextView
android:id="#+id/edtsearch"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="#string/search"
android:layout_weight="1"
android:textSize="#dimen/textsize_edittext"
android:textColor="#color/black"
android:paddingLeft="#dimen/hdpi_4dp"
android:paddingRight="#dimen/hdpi_4dp"
android:layout_marginLeft="#dimen/hdpi_8dp"
android:layout_marginRight="#dimen/hdpi_8dp"
android:singleLine="false"
android:minLines="1"
android:maxLines="5"
>
<requestFocus />
</in.ispg.chipview.ConatctCompleteTextView>
<Button
android:id="#+id/btnsearch"
android:layout_width="fill_parent"
android:layout_height="#dimen/hdpi_33dp"
android:text="#string/done"
android:layout_weight="3"
android:background="#drawable/send_button"
android:layout_marginRight="#dimen/hdpi_8dp"
android:textColor="#color/white"
android:gravity="center"
/>
<in.ispg.utils.FontTextView
android:id="#android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:textSize="#dimen/textsize_edittext"
android:textColor="#595959"
android:textStyle="bold"
android:text="" />
<ProgressBar
android:id="#+id/progressBar1"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:indeterminate="true"
android:indeterminateDrawable ="#drawable/progress"
android:visibility="gone" />
Note
I know how to dismiss the keyboard. that is not my problem. I get a stackoverflow error when i do so in a specific view.

I use this code to hide soft keyboard
// To close the soft keyboard
InputMethodManager inputMethod = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethod.hideSoftInputFromWindow(getView().getWindowToken(), 0);

1.) try this , hope it will solve your issue, i use the below code to HideSoftkeyboard
public void hideSoftKeyboard(Activity activity) {
InputMethodManager inputMethodManager = (InputMethodManager) activity
.getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus()
.getWindowToken(), 0);
}
OR
If I need to care about when the keyboard appears and disappears (which is quite often) then what I do is customize my top-level layout class into one which overrides onMeasure(). The basic logic is that if the layout finds itself filling significantly less than the total area of the window, then a soft keyboard is probably showing.
import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.LinearLayout;
/*
* LinearLayoutThatDetectsSoftKeyboard - a variant of LinearLayout that can detect when
* the soft keyboard is shown and hidden (something Android can't tell you, weirdly).
*/
public class LinearLayoutThatDetectsSoftKeyboard extends LinearLayout {
public LinearLayoutThatDetectsSoftKeyboard(Context context, AttributeSet attrs) {
super(context, attrs);
}
public interface Listener {
public void onSoftKeyboardShown(boolean isShowing);
}
private Listener listener;
public void setListener(Listener listener) {
this.listener = listener;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = MeasureSpec.getSize(heightMeasureSpec);
Activity activity = (Activity)getContext();
Rect rect = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
int statusBarHeight = rect.top;
int screenHeight = activity.getWindowManager().getDefaultDisplay().getHeight();
int diff = (screenHeight - statusBarHeight) - height;
if (listener != null) {
listener.onSoftKeyboardShown(diff>128); // assume all soft keyboards are at least 128 pixels high
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Then in your Activity class...
public class MyActivity extends Activity implements LinearLayoutThatDetectsSoftKeyboard.Listener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
LinearLayoutThatDetectsSoftKeyboard mainLayout = (LinearLayoutThatDetectsSoftKeyboard)findViewById(R.id.main);
mainLayout.setListener(this);
...
}
#Override
public void onSoftKeyboardShown(boolean isShowing) {
// do whatever you need to do here
}
...
}

I had the same problem. I don't know why it happens. I tested in differents devices and in a emulator. This error happens just in some devices and in emulator I can't simulate it. This is what I did:
#Override
public int getSize(Paint paint, CharSequence charSequence, int i, int i2, Paint.FontMetricsInt fm) {
try{
prepView();
}catch(StackOverflowError error){
}
...
This code prevent the application close.

Related

Open bottom sheet when sibling scrolling reaches the end?

Is there any way to "forward" scroll events from one scrolling view to my bottom sheet, so that my bottom sheet begins to expand when I over-scroll the first scrolling view?
Consider this tiny app:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int peekHeight = getResources().getDimensionPixelSize(R.dimen.bottom_sheet_peek_height); // 96dp
View bottomSheet = findViewById(R.id.bottomSheet);
BottomSheetBehavior<View> behavior = BottomSheetBehavior.from(bottomSheet);
behavior.setPeekHeight(peekHeight);
}
}
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- LinearLayout holding children to scroll through -->
</android.support.v4.widget.NestedScrollView>
<View
android:id="#+id/bottomSheet"
android:layout_width="300dp"
android:layout_height="400dp"
android:layout_gravity="center_horizontal"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior"/>
</android.support.design.widget.CoordinatorLayout>
Out of the box, this works just fine. I see 96dp worth of my bottom sheet, and I can swipe it up and down as normal. Additionally, I can see my scrolling content, and I can scroll it up and down as normal.
Let's assume I'm at the state shown in the second image. My NestedScrollView is scrolled all the way to the bottom and my bottom sheet is collapsed. I'd like to be able to swipe upwards on the NestedScrollView (not on the bottom sheet) and, since it can't scroll any farther, have that swipe gesture instead be sent to the bottom sheet, so that it begins to expand. Basically, have the app behave as though my gesture had been performed on the bottom sheet, not the scroll view.
My first thought was to look at NestedScrollView.OnScrollChangeListener, but I couldn't get that to work since it stops being triggered at the boundaries of the scrolling content (after all, it listens for scroll changes, and nothing's changing when you're at the edges).
I also took a look at creating my own subclass of BottomSheetBehavior and trying to override onInterceptTouchEvent(), but ran into trouble in two places. First, I only want to capture events when the sibling scroll view is at the bottom, and I could do that, but I was now capturing all events (making it impossible to scroll the sibling back up). Second, the private field mIgnoreEvents inside BottomSheetBehavior was blocking the bottom sheet from actually expanding. I can use reflection to access this field and prevent it from blocking me, but that feels evil.
Edit: I spent some more time looking into AppBarLayout.ScrollingViewBehavior, since that seemed to be pretty close to what I wanted (it converts swipes on one view into resizing on another), but that appears to manually set the offset pixel by pixel, and bottom sheets don't quite behave that way.
This is an update with a more general solution. It now handles being hidden and "skip collapsed" of the standard bottom view behavior.
The following solution uses a custom BottomSheetBehavior. Here is a quick video of a small app based upon your posted app with the custom behavior in place:
MyBottomSheetBehavior extends BottomSheetBehavior and does the heavy lifting for the desired behavior. MyBottomSheetBehavior is passive until the NestedScrollView reaches its bottom scroll limit. onNestedScroll() identifies that the limit has been reached and offsets the bottom sheet by the amount of the scroll until the offset for the fully expanded bottom sheet is reached. This is the expansion logic.
Once the bottom sheet is released from the bottom, the bottom sheet is considered "captured" until the user lifts a finger from the screen. While the bottom sheet is captured, onNestPreScroll() handles moving the bottom sheet toward the bottom of the screen. This is the collapsing logic.
BottomSheetBehavior doesn't provide a means to manipulate the bottom sheet other than to completely collapse or expand it. Other functionality that is needed is locked up in package-private functions of the base behavior. To get around this, I created a new class called BottomSheetBehaviorAccessors that shares a package (android.support.design.widget) with the stock behavior. This class provides access to some package-private methods that are used in the new behavior.
MyBottomSheetBehavior also accommodates the callbacks of BottomSheetBehavior.BottomSheetCallback and other general functionality.
MyBottomSheetBehavior.java
public class MyBottomSheetBehavior<V extends View> extends BottomSheetBehaviorAccessors<V> {
// The bottom sheet that interests us.
private View mBottomSheet;
// Offset when sheet is expanded.
private int mMinOffset;
// Offset when sheet is collapsed.
private int mMaxOffset;
// This is the bottom of the bottom sheet's parent.
private int mParentBottom;
// True if the bottom sheet is being moved through nested scrolls from NestedScrollView.
private boolean mSheetCaptured = false;
// True if the bottom sheet is touched directly and being dragged.
private boolean mIsheetTouched = false;
// Set to true on ACTION_DOWN on the NestedScrollView
private boolean mScrollStarted = false;
#SuppressWarnings("unused")
public MyBottomSheetBehavior() {
}
#SuppressWarnings("unused")
public MyBottomSheetBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
mSheetCaptured = false;
mIsheetTouched = parent.isPointInChildBounds(child, (int) ev.getX(), (int) ev.getY());
mScrollStarted = !mIsheetTouched;
}
return super.onInterceptTouchEvent(parent, child, ev);
}
#Override
public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
mMinOffset = Math.max(0, parent.getHeight() - child.getHeight());
mMaxOffset = Math.max(parent.getHeight() - getPeekHeight(), mMinOffset);
mBottomSheet = child;
mParentBottom = parent.getBottom();
return super.onLayoutChild(parent, child, layoutDirection);
}
#Override
public void onNestedPreScroll(#NonNull CoordinatorLayout coordinatorLayout,
#NonNull V child, #NonNull View target, int dx, int dy,
#NonNull int[] consumed, int type) {
if (dy >= 0 || !mSheetCaptured || type != ViewCompat.TYPE_TOUCH
|| !(target instanceof NestedScrollView)) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
return;
}
// Pointer moving downward (dy < 0: scrolling toward top of data)
if (child.getTop() - dy <= mMaxOffset) {
// Dragging...
ViewCompat.offsetTopAndBottom(child, -dy);
setStateInternalAccessor(STATE_DRAGGING);
consumed[1] = dy;
} else if (isHideable()) {
// Hide...
ViewCompat.offsetTopAndBottom(child, Math.min(-dy, mParentBottom - child.getTop()));
consumed[1] = dy;
} else if (mMaxOffset - child.getTop() > 0) {
// Collapsed...
ViewCompat.offsetTopAndBottom(child, mMaxOffset - child.getTop());
consumed[1] = dy;
}
if (consumed[1] != 0) {
dispatchOnSlideAccessor(child.getTop());
}
}
#Override
public void onNestedScroll(#NonNull CoordinatorLayout coordinatorLayout, #NonNull V child,
#NonNull View target, int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed, int type) {
if (dyUnconsumed <= 0 || !(target instanceof NestedScrollView)
|| type != ViewCompat.TYPE_TOUCH || getState() == STATE_HIDDEN) {
mSheetCaptured = false;
} else if (!mSheetCaptured) {
// Capture the bottom sheet only if it is at its collapsed height.
mSheetCaptured = isSheetCollapsed();
}
if (!mSheetCaptured) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed, type);
return;
}
/*
If the pointer is moving upward (dyUnconsumed > 0) and the scroll view isn't
consuming scroll (dyConsumed == 0) then the scroll view must be at the end
of its scroll.
*/
if (child.getTop() - dyUnconsumed < mMinOffset) {
// Expanded...
ViewCompat.offsetTopAndBottom(child, mMinOffset - child.getTop());
} else {
// Dragging...
ViewCompat.offsetTopAndBottom(child, -dyUnconsumed);
setStateInternalAccessor(STATE_DRAGGING);
}
dispatchOnSlideAccessor(child.getTop());
}
#Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
if (mScrollStarted) {
// Ignore initial call to this method before anything has happened.
mScrollStarted = false;
} else if (!mIsheetTouched) {
snapBottomSheet();
}
super.onStopNestedScroll(coordinatorLayout, child, target);
}
private void snapBottomSheet() {
if ((mMaxOffset - mBottomSheet.getTop()) > (mMaxOffset - mMinOffset) / 2) {
setState(BottomSheetBehavior.STATE_EXPANDED);
} else if (shouldHideAccessor(mBottomSheet, 0)) {
setState(BottomSheetBehavior.STATE_HIDDEN);
} else {
setState(BottomSheetBehavior.STATE_COLLAPSED);
}
}
private boolean isSheetCollapsed() {
return mBottomSheet.getTop() == mMaxOffset;
}
#SuppressWarnings("unused")
private static final String TAG = "MyBottomSheetBehavior";
}
BottomSheetBehaviorAccessors
package android.support.design.widget; // important!
// A "friend" class to provide access to some package-private methods in `BottomSheetBehavior`.
public class BottomSheetBehaviorAccessors<V extends View> extends BottomSheetBehavior<V> {
#SuppressWarnings("unused")
protected BottomSheetBehaviorAccessors() {
}
#SuppressWarnings("unused")
public BottomSheetBehaviorAccessors(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void setStateInternalAccessor(int state) {
super.setStateInternal(state);
}
protected void dispatchOnSlideAccessor(int top) {
super.dispatchOnSlide(top);
}
protected boolean shouldHideAccessor(View child, float yvel) {
return mHideable && super.shouldHide(child, yvel);
}
#SuppressWarnings("unused")
private static final String TAG = "BehaviorAccessor";
}
MainActivity.java
public class MainActivity extends AppCompatActivity{
private View mBottomSheet;
MyBottomSheetBehavior<View> mBehavior;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayShowTitleEnabled(false);
int peekHeight = getResources().getDimensionPixelSize(R.dimen.bottom_sheet_peek_height); // 96dp
mBottomSheet = findViewById(R.id.bottomSheet);
mBehavior = (MyBottomSheetBehavior) MyBottomSheetBehavior.from(mBottomSheet);
mBehavior.setPeekHeight(peekHeight);
}
}
activity_main.xml
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="#+id/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stateListAnimator="#null"
android:theme="#style/AppTheme.AppBarOverlay"
app:expanded="false"
app:layout_behavior="android.support.design.widget.AppBarLayout$Behavior">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsingToolbarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:statusBarScrim="?attr/colorPrimaryDark">
<ImageView
android:layout_width="match_parent"
android:layout_height="250dp"
android:layout_marginTop="?attr/actionBarSize"
android:scaleType="centerCrop"
android:src="#drawable/seascape1"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="1.0"
tools:ignore="ContentDescription" />
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<com.example.bottomsheetoverscroll.MyNestedScrollView
android:id="#+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#android:color/holo_blue_light" />
<View
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#android:color/holo_red_light" />
<View
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#android:color/holo_blue_light" />
<View
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#android:color/holo_red_light" />
<View
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#android:color/holo_blue_light" />
<View
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#android:color/holo_red_light" />
<View
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#android:color/holo_green_light" />
</LinearLayout>
</com.example.bottomsheetoverscroll.MyNestedScrollView>
<TextView
android:id="#+id/bottomSheet"
android:layout_width="300dp"
android:layout_height="400dp"
android:layout_gravity="center_horizontal"
android:background="#android:color/white"
android:text="Bottom Sheet"
android:textAlignment="center"
android:textSize="24sp"
android:textStyle="bold"
app:layout_behavior="com.example.bottomsheetoverscroll.MyBottomSheetBehavior" />
<!--app:layout_behavior="android.support.design.widget.BottomSheetBehavior" />-->
</android.support.design.widget.CoordinatorLayout>

Expand and collapse Relativelayout by button click

I have this RelativeLayout which expand and collapse on button click
it works fine on one button.
I want to reuse same method on more two RelativeLayout
in same layout
and expand using other two buttons.
This code is running fine. just want more layout to do same action.
Layout:
This is my code:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="64dp"
android:background="#FFF"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Title"
android:textSize="20sp" />
<Button
android:id="#+id/viewmore"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_marginLeft="280dp"
android:background="#null"
android:text="viewmore" />
</RelativeLayout>
<RelativeLayout
android:visibility="gone"
android:id="#+id/expandable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:background="#color/colorAccent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="133dp"
android:text="Text messaging, or texting, is the act of composing and sending electronic messages, typically consisting of alphabetic and numeric characters"
android:textSize="20sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/textView4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Title 2"
android:textSize="20sp" />
<Button
android:id="#+id/viewmore1"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_marginLeft="280dp"
android:background="#null"
android:text="viewmore" />
</RelativeLayout>
<RelativeLayout
android:visibility="gone"
android:animateLayoutChanges="true"
android:id="#+id/expandable1"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginTop="30dp"
android:background="#color/colorPrimary">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Text messaging, or texting, is the act of composing and sending electronic messages, typically consisting of alphabetic and numeric characters"
android:textSize="20sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Title 3"
android:textSize="20sp" />
<Button
android:id="#+id/viewmore2"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_marginLeft="280dp"
android:background="#null"
android:text="viewmore" />
</RelativeLayout>
<RelativeLayout
android:visibility="gone"
android:animateLayoutChanges="true"
android:id="#+id/expandable2"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginTop="30dp"
android:background="#color/colorPrimary">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Text messaging, or texting, is the act of composing and sending electronic messages, typically consisting of alphabetic and numeric characters"
android:textSize="20sp" />
</RelativeLayout>
</LinearLayout>
</ScrollView>
Source Code:
RelativeLayout relativeLayout, relativeLayout1, relativeLayout2;
Button viewmore, viewmore1, viewmore2;
ValueAnimator mAnimator;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewmore);
relativeLayout = (RelativeLayout) findViewById(R.id.expandable);
relativeLayout1 = (RelativeLayout) findViewById(R.id.expandable1);
relativeLayout2 = (RelativeLayout) findViewById(R.id.expandable2);
viewmore = (Button) findViewById(R.id.viewmore);
viewmore1 = (Button) findViewById(R.id.viewmore1);
viewmore2 = (Button) findViewById(R.id.viewmore2);
viewmore.setOnClickListener(this);
viewmore1.setOnClickListener(this);
viewmore2.setOnClickListener(this);
relativeLayout.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
relativeLayout.getViewTreeObserver().removeOnPreDrawListener(this);
relativeLayout.setVisibility(View.GONE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
relativeLayout.measure(widthSpec, heightSpec);
mAnimator = slideAnimator(0, relativeLayout.getMeasuredHeight());
return true;
}
});
}
private void expand() {
relativeLayout.setVisibility(View.VISIBLE);
mAnimator.start();
}
private void collapse() {
int finalHeight = relativeLayout.getHeight();
ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
mAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationEnd(Animator animator) {
//Height=0, but it set visibility to GONE
relativeLayout.setVisibility(View.GONE);
}
#Override
public void onAnimationStart(Animator animator) {
}
#Override
public void onAnimationCancel(Animator animator) {
}
#Override
public void onAnimationRepeat(Animator animator) {
}
});
mAnimator.start();
}
private ValueAnimator slideAnimator(int start, int end) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//Update Height
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = relativeLayout.getLayoutParams();
layoutParams.height = value;
relativeLayout.setLayoutParams(layoutParams);
}
});
return animator;
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.viewmore:
if (relativeLayout.getVisibility() == View.GONE) {
expand();
} else {
collapse();
}
break;
case R.id.viewmore1:
break;
case R.id.viewmore2:
break;
}
}
To continue with your approach, you will have to make the code apply to all three sections that you have laid out. To do this, you will need to change several of your methods to accept a RelativeLayout as an argument.
First, in your onClick listener, fill in the case blocks so each block calls expand() with the targeted RelativeLayout and maximum height. Call collapse() with the targeted RelativeLayout. You will then need to modify expand() and collapse() to handle the new arguments:
You will notice in the following code that I have changed how and where the animator is created. The animator will need to work with each RelativeLayout.
So, onClick() calls expand() which calls slideAnimator(). For each call, the RelativeLayout that is effected is passed as an argument. In this way, you can generalize the code to work with more than one RelativeLayout.
The pre-draw listener will also need to measure each expandable RelativeLayout.
Here is it all put together:
MainActivity.xml
public class MainActivity extends AppCompatActivity
implements View.OnClickListener {
RelativeLayout relativeLayout, relativeLayout1, relativeLayout2;
Button viewmore, viewmore1, viewmore2;
int height, height1, height2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewmore);
relativeLayout = (RelativeLayout) findViewById(R.id.expandable);
relativeLayout1 = (RelativeLayout) findViewById(R.id.expandable1);
relativeLayout2 = (RelativeLayout) findViewById(R.id.expandable2);
viewmore = (Button) findViewById(R.id.viewmore);
viewmore1 = (Button) findViewById(R.id.viewmore1);
viewmore2 = (Button) findViewById(R.id.viewmore2);
viewmore.setOnClickListener(this);
viewmore1.setOnClickListener(this);
viewmore2.setOnClickListener(this);
relativeLayout.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
relativeLayout.getViewTreeObserver().removeOnPreDrawListener(this);
relativeLayout.setVisibility(View.GONE);
relativeLayout1.setVisibility(View.GONE);
relativeLayout2.setVisibility(View.GONE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
relativeLayout.measure(widthSpec, heightSpec);
height = relativeLayout.getMeasuredHeight();
relativeLayout1.measure(widthSpec, heightSpec);
height1 = relativeLayout.getMeasuredHeight();
relativeLayout2.measure(widthSpec, heightSpec);
height2 = relativeLayout.getMeasuredHeight();
return true;
}
});
}
private void expand(RelativeLayout layout, int layoutHeight) {
layout.setVisibility(View.VISIBLE);
ValueAnimator animator = slideAnimator(layout, 0, layoutHeight);
animator.start();
}
private void collapse(final RelativeLayout layout) {
int finalHeight = layout.getHeight();
ValueAnimator mAnimator = slideAnimator(layout, finalHeight, 0);
mAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationEnd(Animator animator) {
//Height=0, but it set visibility to GONE
layout.setVisibility(View.GONE);
}
#Override
public void onAnimationStart(Animator animator) {
}
#Override
public void onAnimationCancel(Animator animator) {
}
#Override
public void onAnimationRepeat(Animator animator) {
}
});
mAnimator.start();
}
private ValueAnimator slideAnimator(final RelativeLayout layout, int start, int end) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//Update Height
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = layout.getLayoutParams();
layoutParams.height = value;
layout.setLayoutParams(layoutParams);
}
});
return animator;
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.viewmore:
if (relativeLayout.getVisibility() == View.GONE) {
expand(relativeLayout, height);
} else {
collapse(relativeLayout);
}
break;
case R.id.viewmore1:
if (relativeLayout1.getVisibility() == View.GONE) {
expand(relativeLayout1, height1);
} else {
collapse(relativeLayout1);
}
break;
case R.id.viewmore2:
if (relativeLayout2.getVisibility() == View.GONE) {
expand(relativeLayout2, height2);
} else {
collapse(relativeLayout2);
}
break;
}
}
}
You can also create own custom expandable which extend android relative layout. On that custom view you can store expanded or collapsed status. As well as you can create custom attributes for define your view default status like expanded or collapsed. So you don't need to compare view status you will just call your toggle function which toggle your view expanded to collapse or vice versa
If you want to show collapsed view as a default view you should not change view visibility before onMeasure function and store your view measured height. If you change visibility on view constructor onMeasure function skip calculation for that view. You should toggle visibility on onPreDraw function.

Show value when tapped [MPAndroidChart]

I`ve been looking for a way to enable the MPAndroidChart to only display the value(label) of data point when clicked. But It seems that I could not find it online even in the documentation.
I used the line chart and what I want is to only display the label of the certain point when clicked.
1- Enable touch in the chart
chart.setTouchEnabled(true);
2 - Create MarkerView
public class CustomMarkerView extends MarkerView {
private TextView tvContent;
public CustomMarkerView (Context context, int layoutResource) {
super(context, layoutResource);
// this markerview only displays a textview
tvContent = (TextView) findViewById(R.id.tvContent);
}
// callbacks everytime the MarkerView is redrawn, can be used to update the
// content (user-interface)
#Override
public void refreshContent(Entry e, Highlight highlight) {
tvContent.setText("" + e.getVal()); // set the entry-value as the display text
}
#Override
public int getXOffset() {
// this will center the marker-view horizontally
return -(getWidth() / 2);
}
#Override
public int getYOffset() {
// this will cause the marker-view to be above the selected value
return -getHeight();
}
}
3 - Create the tvContent view
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:background="#drawable/markerImage" >
<TextView
android:id="#+id/tvContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="7dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:text=""
android:textSize="12dp"
android:textColor="#android:color/white"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
4. Set the view Marker in the chart
CustomMarkerView mv = new CustomMarkerView (Context, R.layout.custom_marker_view_layout);
chart.setMarkerView(mv);
https://github.com/PhilJay/MPAndroidChart/wiki/MarkerView
Use IMarker Interface (MarkerView has been deprecated since release 3.0.0)
1. Create a new class that implements the IMarker interface
public class YourMarkerView extends MarkerView {
private TextView tvContent;
public MyMarkerView(Context context, int layoutResource) {
super(context, layoutResource);
// find your layout components
tvContent = (TextView) findViewById(R.id.tvContent);
}
// callbacks everytime the MarkerView is redrawn, can be used to update the
// content (user-interface)
#Override
public void refreshContent(Entry e, Highlight highlight) {
tvContent.setText("" + e.getY());
// this will perform necessary layouting
super.refreshContent(e, highlight);
}
private MPPointF mOffset;
#Override
public MPPointF getOffset() {
if(mOffset == null) {
// center the marker horizontally and vertically
mOffset = new MPPointF(-(getWidth() / 2), -getHeight());
}
return mOffset;
}}
2. set your marker to the chart
IMarker marker = new YourMarkerView();
chart.setMarker(marker);
Reference: https://github.com/PhilJay/MPAndroidChart/wiki/IMarker-Interface
Example in Kotlin for future you:
Define marker layout i.e. gold_marker_layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="#+id/tvContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="6828.1465"
android:textSize="12sp"
android:textColor="#android:color/white"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Quick note: I set the initial text to 6828.1465 because that's my max-width text - watch out here.
If you don't need getXOffset and getYOffset you can assign marker like that:
lineChart.marker = object : MarkerView(context, R.layout.gold_marker_layout) {
override fun refreshContent(e: Entry, highlight: Highlight) {
(findViewById<View>(R.id.tvContent) as TextView).text = "${e.y}"
}
}
That's it, you should be good to go

popup window over canvas Android

I am building an application which display a map (the map is the canvas background) and localise users by adding circle on the canvas(using draw circle). I would like to add a button over the canvas(for now a draw a button on the map and check with ontouch() if the user clicked on it) and when the button is touched I would like to have a window popup. It worked but the popup is behind the canvas(I could see a small piece of it(I removed it)).Is there a way to have my canvas BEHIND the button and the popup window? I saw people talking about putting the canvas in relative layout but I have no idea how to do that.
Here is the xml of my activity, really simple:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#drawable/umap2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:text="Button" />
</RelativeLayout>
And here is my activity java code(I removed a couple of things that doesnt have nothing to do with my problem)
package com.example.client;
import java.util.LinkedList;
//....
import java.util.Timer;
public class Campus extends Activity{
final Handler myHandler = new Handler();
MapView mapv;
final Activity self = this;
Float ratioX;
Float ratioY;
int width;
int height;
static boolean out=false;
Intent i;
//creating a linked list for each hall
static LinkedList<compMac> DMS = new LinkedList<compMac>();
static LinkedList<compMac> MCD = new LinkedList<compMac>();
//...
static LinkedList<compMac> SCI = new LinkedList<compMac>();
static LinkedList<compMac> STE = new LinkedList<compMac>();
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.campus);
setSize();
this.mapv = new MapView(this);//!! my view
setContentView(mapv);
i= new Intent(this, myService.class);
this.startService(i);
}
//*******************************View class!*******************************
public class MapView extends View {
/*
* Extract the connected users and location from the array. separate the
* array into an array for each building
* */
private Paint redPaint;
private float radius;
Canvas canvas;
public MapView(Context context) {
super(context) ;
redPaint = new Paint();
redPaint.setAntiAlias(true);
redPaint.setColor(Color.RED);
redPaint.setTextSize(10);
}
#Override
//drawing a point on every hall on the map where users are connected
public void onDraw (Canvas canvas) {
// draw your circle on the canvas
if(!out)
{
AlertDialog.Builder outOfCampus = new AlertDialog.Builder(self);
outOfCampus.setTitle("Sorry");
outOfCampus.setMessage("You are out of Campus");//(toDisplay);
outOfCampus.setCancelable(false);
outOfCampus.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
startActivity(new Intent("com.example.client.Sin"));
}});
AlertDialog alertdialog = outOfCampus.create();
outOfCampus.show();
}
this.canvas=canvas;
setBackgroundResource(R.drawable.umap2);
}
public void drawPoints(LinkedList<compMac> building)
{
if(!building.isEmpty())
{
while(!building.isEmpty())
{
compMac current = building.pop();
Float x= ratioX*(Float.parseFloat(current.getCoorX()));
Float y= ratioY*(Float.parseFloat(current.getCoorY()));
// Log.w("ratioX ",(((Double)(width/768)).toString()));
// Log.w("ratioY ",(float)(y.toString()));
canvas.drawCircle (x,y, 10, redPaint);
}
}
}
public boolean onTouchEvent (MotionEvent event) {
//...//
return true;
}
}
}
Someone have an idea how i can do that? Thanks
Calling setContentView two times would not work. Instead you should put your canvas view and the button in a single layout itself but with proper ordering. The last widget in the relative layout gets more priority, so if you want the button to come on top of the canvas your layout should be something like this.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#drawable/umap2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.example.client.MapView
android:id="#+id/mapView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:text="Button" />
</RelativeLayout>
And to access your MapView in java class
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.campus);
setSize();
this.mapv = findViewById(R.id.mapView); //!! my view
i= new Intent(this, myService.class);
this.startService(i);
}
And obviously alert dialog will be on top of the canvas. Hope it helps!
Edit: I think inflate error is due to incorrect class path. Since MapView is inner class of Campus, path should be like this
<com.example.client.Campus.MapView
android:id="#+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Also add this constructor to your MapView class
public MapView(Context context, AttributeSet attributeSet) {
super(context, attributeSet) ;
redPaint = new Paint();
redPaint.setAntiAlias(true);
redPaint.setColor(Color.RED);
redPaint.setTextSize(10);
}
<FrameLayout 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" >
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#drawable/umap2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<RelativeLayout
android:id="#+id/btn_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:background="#drawable/back_transparent_pressed" >
<Button
android:id="#+id/button1"
android:layout_centerInParent="true"
/>
</RelativeLayout>

Android : custom Drawable for the layout background . Child items are not clear?

My layout file is like this :
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="282dp"
android:layout_height="wrap_content"
android:layout_x="20dp"
android:layout_y="208dp"
android:alpha="155"
android:orientation="vertical" >
<EditText
android:id="#+id/txtUserName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="155"
android:hint="#string/hintUsername"
android:padding="2dp" >
<requestFocus />
</EditText>
<EditText
android:id="#+id/txtPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="155"
android:hint="#string/hintPassword"
android:inputType="textPassword"
android:padding="2dp" />
<Button
android:id="#+id/butBrowse"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="255"
android:text="#string/but_browse" />
</LinearLayout>
And I set a custom Drawable to the layout programatically as follows :
ViewGroup layoutView = (ViewGroup) getLayoutInflater().inflate(
R.layout.login_layout, null); // (ViewGroup)
layoutView.setBackground(new CustomDrawable(this) {
});
private class CustomDrawable extends Drawable {
private Context ctx;
private Bitmap bitmap;
public CustomDrawable(Context ctx) {
this.ctx = ctx;
init();
}
private void init() {
//draw dynamic stuff to the 'bitmap'
}
#Override
public void draw(Canvas canvas) {
canvas.save();
Paint p = new Paint();
canvas.drawBitmap(this.bitmap,0,0,p);
canvas.restore();
}
#Override
public int getOpacity() {
return 15;
}
#Override
public void setAlpha(int alpha) {
}
#Override
public void setColorFilter(ColorFilter cf) {
}
}
Now my login_layout looks like this:
As can be seen in the picture , my child elements (edittext,password area, button) are not clearly visible.
My questions :
1.How to achieve something like above ?Do I have to apply another Drawable to the inner 'LinearLayout' ?
2.Is it possible to change the alpha level of the Textfields,Button so that it is more visible ?
you need to change the opacity for your custom drawable which is currently set to 15. Increasing the opacity will make it more visible.
#Override
public int getOpacity() {
return 15; // increase this value
}

Categories

Resources