Android view can't have focus in accessbility mode - android

below is what my view look like
<ConstraintLayout>
<FrameLayout
android:id = "#+id/a"/>
<FrameLayout
android:id = "#+id/b"/>
</ConstraintLayout>
when i swipe right in talkback mode,it can't give focus from a to b
i set a.accessibilityTravaBefore = b, b.accessibilityTraversalAfter = a

Related

Translation and scale layout Animation Problem in Android

I have the view with layout below.
What I want is moving View B to the position of View A and at the same time move RelativeLayout C up with the same height of View B. The two actions will be done with the animations. Like the picture shown below.
I am using ObjectAnimation to implement this feature, but when I am using the
float viewBY = viewB.getTranslationY();
ObjectAnimator viewBMoveUp
= ObjectAnimator.ofFloat(viewB, "translationY",
viewBY, viewBY - viewB.getHeight());
float layoutCCurrentY = layoutC.getY();
ObjectAnimator layoutCMoveUp
= ObjectAnimator.ofFloat(layoutC, "Y",
layoutCCurrentY, layoutCCurrentY - viewB.getHeight());
AnimatorSet animSet = new AnimatorSet();
animSet.play(viewBMoveUp).with(layoutCMoveUp);
animSet.setDuration(150);
animSet.start();
I find the LayoutC's bottom is also up with viewB.getHeight(), which is not I expected. Like the picture below:
So anybody can help about this?
One simpler way to achieve the expected result is to use animateLayoutChanges attribute in the parent layout.
<!-- Defines whether changes in layout (caused by adding and removing items) should cause
a LayoutTransition to run. When this flag is set to true, a default LayoutTransition object
will be set on the ViewGroup container and default animations will run when these layout
changes occur.-->
<attr name="animateLayoutChanges" format="boolean" />
With this, you don't need to manually animate each movement.
When you set viewA visibility to View.GONE, the parent layout will fade out viewA and translate position of viewB and layoutC.
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true">
<View
android:id="#+id/viewA"
android:layout_width="match_parent"
android:layout_height="60dp" />
<View
android:id="#+id/viewB"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_below="#+id/viewA" />
<RelativeLayout
android:id="#+id/layoutC"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/viewB"
android:layout_alignParentBottom="true">
</RelativeLayout>
</RelativeLayout>

How to animate height change of views inside a LinearLayout?

I am working on an Android 4+ app which uses a quite simply layout: Multiple views are stacked using a LinearLayout within a ScrollView
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="HardcodedText,UseCompoundDrawables,UselessParent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusableInTouchMode="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
<!-- Top Container -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
<Button... />
</LinearLayout>
<!-- Hidden Container -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
... Some Content ...
</LinearLayout>
<!-- Bottom Container -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
... Some Content ...
</LinearLayout>
</LinearLayout>
</ScrollView>
The HiddenContainer should not be visible when the layout it created. Thus in the beginning the BottomContainer is directly beneath the TopContainer. A click on the Button within the TopContainer toggles the visibility of the HiddenContainer.
Doing this with hiddenContainer.setVisibility(hiddenContainer.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE) works find and without any problem. However it does not look good when the view suddenly appears or disappears. Instead I would like to animate the change.
I was surprised that I was not able to find an easy solution for this:
Using android:animateLayoutChanges="true" does work, however I am not able to control the animation.
While using a ValueAnimator to change hiddenContainer.setScaleY(...) gives me control over the animation setScaleY(0) makes the container invisible without reducing the space it occupies within the layout.
Using the ValueAnimator to change hiddenContainer.setHeight(...) might work, however I don't want to use a fixed height value when showing the container (e.g. hiddenContainer.setHeight(300)) but the height which is determined by the containers content.
So, how to solve this?
For animate your changes of layout (alpha, visibility, height, etc) you can use TransitionManager. For example: I have three static methods and use them when I want to animate layout changes:
public static final int DURATION = 200;
public static void beginAuto(ViewGroup viewGroup) {
AutoTransition transition = new AutoTransition();
transition.setDuration(DURATION);
transition.setOrdering(TransitionSet.ORDERING_TOGETHER);
TransitionManager.beginDelayedTransition(viewGroup, transition);
}
public static void beginFade(ViewGroup viewGroup) {
Fade transition = new Fade();
transition.setDuration(DURATION);
TransitionManager.beginDelayedTransition(viewGroup, transition);
}
public static void beginChangeBounds(ViewGroup viewGroup) {
ChangeBounds transition = new ChangeBounds();
transition.setDuration(DURATION);
TransitionManager.beginDelayedTransition(viewGroup, transition);
}
And when you want to animate layout changes you can just call one of this methods before layout changings:
beginAuto(hiddenContainerParentLayout);
hiddenContainer.setVisibility(hiddenContainer.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE)

Shared element animation between RecyclerView item and CollapsingToolbar within same activity

In my application I have a list of items displayed through a RecyclerView adapter. If I click on an item a new Fragment in started within the same Activity. The layout of my item and my Activity look (simplified) like this:
Activity layout:
<android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.AppBarLayout>
<android.support.design.widget.CollapsingToolbarLayout>
<ImageView
android:id="#+id/image"
android:transitionName="image" ... />
<android.support.v7.widget.Toolbar ... />
</android.support.design.widget.CollapsingToolbarLayout>
<android.support.design.widget.TabLayout ... />
</android.support.design.widget.AppBarLayout>
<FrameLayout... />
</android.support.design.widget.CoordinatorLayout>
Item Layout:
<RelativeLayout >
<ImageView
android:id="#id/itemImage"
android:transitionName="image" />
<LinearLayout>
<TextView ... />
<TextView ... />
</LinearLayout>
</RelativeLayout>
Now, if the new fragment is started by an item click, I would like to add an animation of the item image to the ImageView in the CollapsingToolbarLayout. I read the article about ShareElement animations but this does not work here because this is not a real ShareElement animation. The target ImageView is not in the new fragment neither I have to start a new activity (I only make the target ImageView visible in the new Fragment). So how would I create such an animation in this case?
So, you are trying to animate a view from one layout to another.
I think this can be achieved using ViewOverlays API. You can see a detailed answer about that API here.
Now in your case, what you'll end up with is you'd add your ImageView to the ViewGroupOverlay of the root layout:
final ViewGroup container = (ViewGroup) findViewById(R.id.content);
container.getOverlay().add(imageView);
...
From the docs:
If the view has a parent, the view will be removed from that parent before being added to the overlay.
Thus, as soon as you perform getOverlay().add(imageView) the view would be removed from it's parent. Now you are free to create your animation and move the imageView to the final destination.
final ViewGroup container = (ViewGroup) findViewById(R.id.content);
container.getOverlay().add(imageView);
// animate imageView by any API, e.g. ViewPropertyHolder
imageView.animate()
.x(finalX)
.y(finalY)
.setDuration(duration)
.setInterpolator(interpolator)
.withEndAction(() -> {
// crucial point, remove the overlay
container.getOverlay().remove(imageView);
// add this `imageView` to the destination layout
destLayout.addView(imageView);
})
Here's a similar feature you're trying to implement:

Request focus of view and place it on top of the screen within a ScrollView

See bottom of this entry for an answer to this "problem".
In my app I inflate some xml views and add them to a LinearLayout, list, within a ScrollView. The XML looks like this:
<ScrollView
android:layout_width="0dp"
android:layout_weight="19"
android:layout_height="wrap_content"
android:fillViewport="true">
<LinearLayout
android:id="#+id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
I then try to give focus to one of the views I've put into 'list'. This is done by using this code :
view.getParent().requestChildFocus(view, view);
If the above code results in a scroll down, the focused view get placed at the bottom of the screen and if it result in a scroll up, the focused view get placed at the top of the screen.
Is there a way to make the focused view always get placed at the top of the screen if the length of the list permits it?
Edit: This works! See accepted answer.
XML
<ScrollView
android:id="#+id/scroll_list"
android:layout_width="0dp"
android:layout_weight="19"
android:layout_height="wrap_content"
android:fillViewport="true">
<LinearLayout
android:id="#+id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
Sample code to put somewhere in your activity or fragment
view = findViewById(get_view_that_should_have_focus);
ScrollView scrollView = findViewById(R.id.scroll_list);
scrollView.smoothScrollTo(0, view.getTop());
You can vertical scroll to specific view by using this:
scrollView.smoothScrollTo(0,view.getTop());
It will make your view top of the scrollview(if there is enough height) After scrolling, you can request focus for this view.
Since I can't comment, I'm just gonna post this here.
The solution provided by Oğuzhan Döngül is correct, but if some of you can't get it running, try to do the smoothScroll inside runnable :
val topOffset = view.height
scrollView.post {
scrollView.smoothScrollTo(0, view.top - topOffset)
}
I add topOffset to give some spaces on top of the highlighted view.
source

Android: Passing touch event to overlapped view

I have a Relative layout which has two child layouts. First child is linear layout which is container of a fragment. The fragment has few buttons. The second child is a linear layout which has a View which is blank and transparent. The second child overlaps the first child.The first child is smaller than second child. I want to send touch events from second child to first child so that those buttons on the fragment which first child contains receives click.
I read few posts on internet for solving my problem but could not solve it.
So far i have overriden dispatchTouchEvent of Activity and have tried to detect whether touch is in bounds of first child and if so i do firstChild.onTouch(ev). Actually i just dont know what to do and just trying get it working. So please, help me.
#Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
float x,y,backViewX,backViewY,frontViewX,frontViewY;
x = ev.getX();
y = ev.getY();
frontViewX = frontView.getX();
frontViewY = frontView.getY();
backViewX = backView.getX();
backViewY = backView.getY();
if(y >= backViewY && y <= (backViewY+backViewHeight))
{
if(x >= backViewX && x<=(backViewX+backViewWidth))
{
return backView.onTouchEvent(ev);
}
}
return super.dispatchTouchEvent(ev);
}
Orignal Layout is not the exact as i described but it is little more complex but the concept is same as i described....i am shortning the layout for simplifying things.The frontLayout here overlaps layoutBackFragmentArea.
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/back_view_background">
<LinearLayout
android:id="#+id/layoutBackFragmentArea"
android:layout_width="match_parent"
android:layout_height="#dimen/back_view_height"
android:orientation="vertical"
android:gravity="center_vertical"
android:background="#android:color/transparent">
</LinearLayout>
<LinearLayout
android:id="#+id/layoutFrontArea"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_vertical"
android:background="#android:color/transparent">
<View
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</RelativeLayout>
layoutBackFragmentArea will contain a fragment which has UI which should receive click events.

Categories

Resources