View over Canvas Visibility issue - android

I have this 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" >
<com.components.game.GameView
android:id="#+id/game_id"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<RelativeLayout
android:id="#+id/ChatLayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:visibility="invisible"
android:focusable="true"
android:focusableInTouchMode="true" >
<Button
android:id="#+id/ChatCancelButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="X" />
<Button
android:id="#+id/ChatOkButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="OK" />
<EditText
android:id="#+id/ChatEditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="#+id/ChatOkButton"
android:layout_toRightOf="#+id/ChatCancelButton"
android:maxLength="50"
android:singleLine="true" />
</RelativeLayout>
</RelativeLayout>
It's a RelativeLayout over a canvas. At start time it's invisible but when a user clicks a button the layout should become visible.
The problem is that it's not becoming visible. The layout is there but it's just not drawing it. If I press the position where the layout should appear it receives the event and opens the keyboard but it's not drawing the whole layout.
What is the problem?
If I set the RelativeLayout to visible at the beginning it works fine. it shows the layout and if I toggle between invisible and visible it works fine.
I made a workaround that almost always works.
I start the layout visible and than do that in the oncreate:
chatLayout.postDelayed(new Runnable() {
#Override
public void run() {
chatLayout.setVisibility(View.INVISIBLE);
}
}, 50);
But I don't like it and want to understand what's the problem.
The code:
It starts from a canvas button which send a message to a handler:
public void showInputLayout() {
Message.obtain(gameHandler, SHOW_INPUT_LAYOUT).sendToTarget();
}
In the handler:
case SHOW_INPUT_LAYOUT:
gameActivity.setChatVisibility(true);
break;
setChatVisibility:
public void setChatVisibility(boolean isVisible) {
int visible = isVisible ? View.VISIBLE : View.INVISIBLE;
chatLayout.setVisibility(visible);
if(isVisible){
chatEditText.setFocusable(true);
chatEditText.requestFocus();
}
}

Add a click listener to RelativeLayout and switch the visibility between GONE and VISIBLE. Try something like this:
int visibility = View.VISIBLE;
RelativeLayout layout = (RelativeLayout)findViewById(R.id.ChatLayout);
layout.setVisibility(visibility);
layout.setOnClickListener(new View.OnClickListener{
public void onClick(View v)
{
if(visibility == View.VISIBLE)
visibility = View.GONE;
else
visibility = View.VISIBLE;
v.setVisibility(visibility);
}
})

I ran into a similar issue recently, and for my case the problem was actually in the onDraw() method of the view underneath (should be com.components.game.GameView in your case). See if you can add calls to Canvas' getSaveCount(), save() and restoreToCount() in your drawing code, similar to this:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int saveCount = canvas.getSaveCount();
canvas.save();
// custom drawing code here ...
// use Region.Op.INTERSECT for adding clipping regions
canvas.restoreToCount(saveCount);
}
I believe what happened was that sometimes the framework set the clipping regions for the elements on top of our Canvas-drawing widget before our onDraw() method is called so we need to make sure that those regions are preserved.
Hope this helps.

Related

How to slide a view out of another view

I have a fragment with a navigation menu at the top-left corner. At the start of the activity, I want to gradually slide a view (let's call it black_view) out of the menu icon.
Here's a rough breakdown of how I want the animation to be in accordance with the images below:
Activity starts as the first image with black_view being invisible.
black_view gradually slides out from behind the menu icon length by length until it gets to the point of the second image.
>>>
What I've tried:
I tried achieving this by using a TranslateAnimation. However, the whole length of black_view shows up at the start of the animation and this is not what I want. I also saw a couple of sliding animation code snippets like this and this, but they all follow the TranlateAnimation model (with the whole length of black_view showing instantly).
How can I achieve this?
PS: If there's any important detail that I failed to add, kindly let me know.
It can be done easily with Slide transition from Transition API. Just use TransitionManager.beginDelayedTransition method then change visibility of black view from GONE to VISIBLE.
import androidx.appcompat.app.AppCompatActivity;
import androidx.transition.Slide;
import androidx.transition.Transition;
import androidx.transition.TransitionManager;
public class MainActivity extends AppCompatActivity {
private ViewGroup parent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
parent = findViewById(R.id.parent);
parent.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
#Override
public boolean onPreDraw() {
parent.getViewTreeObserver().removeOnPreDrawListener(this);
animate();
return true;
}
});
}
private void animate() {
View textView = findViewById(R.id.text);
Transition transition = new Slide(Gravity.LEFT);
transition.setDuration(2000);
transition.setStartDelay(1000);
TransitionManager.beginDelayedTransition(parent, transition);
textView.setVisibility(View.VISIBLE);
}
}
activity_main.xml
<?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">
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="Button" />
<FrameLayout
android:id="#+id/parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/button">
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#000"
android:text="hello world"
android:textColor="#fff"
android:textSize="22sp"
android:visibility="gone" />
</FrameLayout>
</RelativeLayout>
Result:
All classes here are from androix package so code is backward compatible.

Cannot do Alpha Animation with LinearLayout on Android

So I was trying to make a ActionButton like Trello have (click on a button, a black overlay apears, and other buttons appear on the screen).
But I had one weird problem: I cannot make the alpha animation work.
I tryed in two ways:
private void initializeComponent(final Context ctx, final AttributeSet attrs) {
blackOverlay.setAlpha(0.0f);
blackOverlay.setVisibility(View.VISIBLE);
blackOverlay.setClickable(true);
actionButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
final boolean closed = blackOverlay.getAlpha() == 0.0f;
blackOverlay.animate().alpha(closed ? 1f : 0f).setDuration(500).setListener(null);
});
}
So in this way, the view blackOverlay is always visible and the animation should work. But it doesn't. Clicking the button does not make any difference on alpha. The only change is that toggling the button makes the non-overlay content work/stop work (as it should since blackOverlay is clickable).
So I tryed another approach: not setting the alpha to 0.0f so the overlay will start visible to the user. Basically commenting the first line of initializeComponent method.
private void initializeComponent(final Context ctx, final AttributeSet attrs) {
//blackOverlay.setAlpha(0.0f);
blackOverlay.setVisibility(View.VISIBLE);
blackOverlay.setClickable(true);
actionButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
final boolean closed = blackOverlay.getAlpha() == 0.0f;
blackOverlay.animate().alpha(closed ? 1f : 0f).setDuration(500).setListener(null);
}
});
}
In this way, the button only toggles the alpha, it doesn't animate it.
Any ideas? I've already tryed a lot of solutions here on StackOverflow.
Thanks!
This is my xml layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/blackoverlay"
android:background="#color/black_overlay">
</LinearLayout>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/action_button"
android:layout_gravity="bottom"
android:src="#drawable/plus"
android:layout_margin="50dp"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true" />
</RelativeLayout>
OK so I did a MCVE as karaokyo said but the fun thing was: It worked on MCVE.
So I started looking at my entire code and I found that I had a setOnClickListener for a Button that had the same name as action_button. So I just changed the IDs to not be the same and everything is working now.
It seens it was my bad, I'm stupid lol
So for anyone that is reading this: Try to create a MCVE from scratch (new project even) and see if it works. It maybe be something else non-related interfering in your code.

I want to set a transparent background layer when I click on the floating action menu.

This is what I have done so far. Created FloatingActionButton. Now As the + icon is pressed a translucent layer should be there at the back.
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.getbase.floatingactionbutton.FloatingActionsMenu
android:id="#+id/actionMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="60dp"
fab:fab_addButtonColorNormal="#color/primary"
fab:fab_addButtonColorPressed="#color/primary_dark"
fab:fab_addButtonPlusIconColor="#ffffff">
<com.getbase.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="openAudio"
fab:fab_colorNormal="#EA1E63"
fab:fab_colorPressed="#EA1E63"
fab:fab_icon="#drawable/ic_action_mic" />
</com.getbase.floatingactionbutton.FloatingActionsMenu>
</RelativeLayout
Try this.
floatingActionMenuButton.setOnMenuToggleListener(new FloatingActionMenu.OnMenuToggleListener() {
#Override
public void onMenuToggle(boolean opened) {
if (opened) {
//menu opened
} else {
//menu closed
}
}
});
use setBackgroundResource or setBackgroundColor. I think first is pretty simple.
Second one takes an int as an argument. So, just convert your hex color (for example #55000000) into decimal and it will work as well.
I had the same issue and i fixed it in following way.
I added a relative layout which will match parent in both width and height.
Set its background color to black and set alpha to your required opacity.
<RelativeLayout
android:id="#+id/obstructor"
android:visibility="invisible"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:alpha="0.75"
android:background="#android:color/black">
</RelativeLayout>
And then on the menu item expanded and collapsed make this visible and invisible.
mFabMenu = (FloatingActionsMenu) findViewById(R.id.multiple_actions);
final RelativeLayout obstrucuterView = (RelativeLayout) findViewById(R.id.obstructor);
obstrucuterView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (obstrucuterView.getVisibility() == View.VISIBLE) {
mFabMenu.collapse();
return true;
}
return false;
}
});
mFabMenu.setOnFloatingActionsMenuUpdateListener(new FloatingActionsMenu.OnFloatingActionsMenuUpdateListener() {
#Override
public void onMenuExpanded() {
if (obstrucuterView.getVisibility() == View.INVISIBLE)
obstrucuterView.setVisibility(View.VISIBLE);
}
#Override
public void onMenuCollapsed() {
if (obstrucuterView.getVisibility() == View.VISIBLE)
obstrucuterView.setVisibility(View.INVISIBLE);
}
});
Hope this helps.
There is another custom library which is more advanced than what you are using right now.
Get it here
Clans floating Action Button
Set this to true and it will solve your problem. Do let me know if it helps
fabPlusButton.setClosedOnTouchOutside(true);
If you are using Clans floating Action Button then perhaps fab:menu_backgroundColor might be something that you could have a look at if it satisfies your use-case. ofcourse the layout width and height should both match parent (This solution has worked for me)

Translate a View from One Layout to Another with Translate Animation

I am new in Android animation and my requirement is to translate a view from one layout to layout in a single xml file on click of that view.
Scenario:
Suppose I click a button, present on the top of the header in a xml file,and it should move/translate downwards (it should give an impact that it lies on the other layout downwards to header), and also I want that when the user clicks on the same again, it should now move to its original position.
Here I am explaining with my xml file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/app_bg"
android:orientation="vertical" >
<RelativeLayout
android:id="#+id/top"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#drawable/header"
android:paddingLeft="10dp"
android:paddingRight="10dp" >
<Button
android:id="#+id/btnSearchHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_centerInParent="true"
android:background="#drawable/search_icon" />
</RelativeLayout>
<RelativeLayout
android:id="#+id/bottom"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/app_transparent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_marginTop="10dp"
android:visibility="visible" >
<Button
android:id="#+id/btnMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginRight="5dp"
android:text="ABC" />
<Button
android:id="#+id/btnSearchSelected"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/btnMenu"
android:text="CDE" />
</RelativeLayout>
</LinearLayout>
MORE PRECISE REQUIREMENT SPECIFICATION (Kindly read carefully:)
Here I have two sub inner layouts:-
Top Layout - id-> top
Bottom Layout- id -> bottom
Now a view (Button -> btnSearchHeader) is lying in my top layout and I want to animate the same to the bottom layout (it should give an impact that it is translated with a translate animation to the bottom layout) on click of that button and when the user clicks on that button, it should again translate back to its original position with a translate animation .. i.e it should show back in the top layout
I have no idea how to give these impacts using translate animations, however i just have a basic translate animation knowledge which is insufficient for me to work upon my requirement.
Any type of related help is appreciable.
Thanks
Have you tried something simple like the following?
final int topHeight = findViewById(R.id.top).getHeight();
final int bottomHeight = findViewById(R.id.bottom).getHeight();
final View button = findViewById(R.id.btnSearchHeader);
final ObjectAnimator moveDownAnim = ObjectAnimator.ofFloat(button, "translationY", 0.F, topHeight + bottomHeight / 2 - button.getHeight() / 2);
final ObjectAnimator moveUpAnim = ObjectAnimator.ofFloat(button, "translationY", topHeight + bottomHeight / 2 - button.getHeight() / 2, 0.F);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (0.F == v.getTranslationY())
moveDownAnim.start();
else
moveUpAnim.start();
}
});
If you actually need the button view to change parents, you can use AnimatorListener to achieve this at the end of each animation. Something like:
moveDownAnim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationEnd(Animator animation) {
((ViewGroup)findViewById(R.id.top)).removeView(button);
((ViewGroup)findViewById(R.id.bottom)).addView(button);
((RelativeLayout)button.getLayoutParams()).addRule(RelativeLayout.CENTER_IN_PARENT);
button.setTranslationY(0.F); // Since it is now positioned in the new layout, no need for translation.
}
#Override
public void onAnimationCancel(Animator animation) { /* NOP */ }
#Override
public void onAnimationRepeat(Animator animation) { /* NOP */ }
#Override
public void onAnimationStart(Animator animation) { /* NOP */ }
});
(And analogous listener for the moveUpAnim.)
However, I doubt you need to actually do this to achieve the visual effect you want. But if you do this part, you will probably also need to set a fixed height for your top view as opposed to wrap_content. (Otherwise, if a layout pass happens while the button has been moved to the bottom view, the top layout's height might go to 0 if there's nothing else in it.) Easiest would be to just do this directly in the xml layout file. However, if you want to "do it on the fly", you can change the layout's height in the onAnimationEnd() method using something like:
#Override
public void onAnimationEnd(Animator animation) {
final ViewGroup topLayout = findViewById(R.id.top);
topLayout.getLayoutParams().height = topLayout.getHeight(); // Keep it the same height...
topLayout.removeView(button);
((ViewGroup)findViewById(R.id.bottom)).addView(button);
((RelativeLayout)button.getLayoutParams()).addRule(RelativeLayout.CENTER_IN_PARENT);
button.setTranslationY(0.F); // Since it is now positioned in the new layout, no need for translation.
}

HorizontalScrollView with arrows

I'm trying to make a scrollable horizontal menu using HorizontalScrollView using the layout shown below. The menu is scrollable using the previous/next arrow buttons or on fling. When the HorizontalScrollView reach one end I'd like the arrow at the same end to be hidden (in the image shown below I'd like the left arrow to be hidden).
How can I detect that the HorizontalScrollView has reached an end?
Thanks
<RelativeLayout android:background="#drawable/bg_home_menu"
android:layout_width="fill_parent" android:layout_height="45dp">
<ImageView android:id="#+id/previous" android:src="#drawable/arrow_l"
android:layout_height="14dp" android:layout_width="10dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true" />
<ImageView android:id="#+id/next" android:src="#drawable/arrow_r"
android:layout_height="14dp" android:layout_width="10dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true" />
<HorizontalScrollView android:id="#+id/horizontalScroll"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:scrollbars="none" android:fadingEdgeLength="30dp" android:layout_toLeftOf="#id/next"
android:layout_toRightOf="#id/previous" >
<LinearLayout android:id="#+id/home_menu"
android:orientation="horizontal" android:layout_width="wrap_content"
android:layout_height="fill_parent" android:gravity="center_vertical">
<Button android:id="#+id/btn_home" android:background="#drawable/btn_home_menu_on"
android:layout_height="35dp" android:layout_width="wrap_content" android:focusable="true"
android_clickable="false" android:text="#string/menu_home" android:textColor="#FFFFFF" android:tag="-1"/>
<!-- More are added dynamically -->
</LinearLayout>
</HorizontalScrollView>
</RelativeLayout>
There is no scroll listener for the HorizontalScrollView. What you can do is add an OnTouchListener to it This will fire continously when the user scrolls, and each time you can use the method getScrollX which returns the int.
Now 0 mean its left most, to find the right most(max scroll amount), you need to find the width of the child view of the horzontal scroll view. That is found by using the addOnGlobalLayoutListener else the width will always be 0 inside the onCreate method
Put this code in the onCreate method
final HorizontalScrollView hs = (HorizontalScrollView)findViewById(R.id.scroll);
ViewTreeObserver vto = hs.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
hs.getViewTreeObserver().removeGlobalOnLayoutListener(this);
maxScrollX = hs.getChildAt(0)
.getMeasuredWidth()-getWindowManager().getDefaultDisplay().getWidth();
}
});
hs.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("ScrollValue", Integer.toString(hs.getScrollX()));
if(hs.getScrollX() == maxScrollX){
Log.e("MaxRight", "MaxRight");
}
return false;
}
});
Use getScrollX() of the LinearLayout inside your scroller to determine which is the leftmost corner on screen. If it's 0, then left arrow should be disabled.
For the right arrow, retrieve the drawing rectangle's width, add getScrollX() and compare that to getMeasuredWidth (), if it's the same, you're at the right, no need for the right arrow.
So your code will look like this:
if (homeMenu.getScrollX()==0) {
hideLeftArrow();
} else {
showLeftArrow();
}
if (homeMenu.getDrawingRect().right == homeMenu.getMeasuredWidth()) {
hideRightArrow();
} else {
showRightArrow();
}
Of course you will have to put this in an event listener. I can only think of using your own HorizontalScrollView class, with the onScroll() method overwritten to perform the checks above, see this post.

Categories

Resources