Moving a textview next to another textview off-screen - android

I have a relative layout containing three textviews, each having a width of half-width of the screen. I want the user to be able to use a scroll gesture and move these textviews together, and if the textview located far left goes off-screen, it is moved to the far right next to the third textview. So I want to create a sort of a endless scroller-system.
However, using the code below results in gaps between the views when scrolling, and I think the gap widths are dependable on the scrolling speed.
Here is a link to a screenshot of the problem: http://postimg.org/image/bnl0dqsgd/
Currently I have implemented scrolling only for one direction.
XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/rel_layout"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false">
<com.app.healthview.BorderedTextView
android:id="#+id/btvYear1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:background="#color/YearColor1"
android:maxLines="1"
android:text="2012" />
<com.app.healthview.BorderedTextView
android:id="#+id/btvYear2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_alignParentTop="true"
android:layout_toRightOf="#+id/btvYear1"
android:background="#color/YearColor2"
android:maxLines="1"
android:text="2013" />
<com.app.healthview.BorderedTextView
android:id="#+id/btvYear3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/YearColor1"
android:layout_alignParentTop="true"
android:layout_toRightOf="#+id/btvYear2"
android:gravity="center"
android:maxLines="1"
android:text="2014" />
</RelativeLayout>
Then I initialize the views in a function, which is called after setting the content view:
public void InitTimeView() {
year_views = new BorderedTextView[3];
year_views[0] = (BorderedTextView) findViewById(R.id.btvYear1);
year_views[1] = (BorderedTextView) findViewById(R.id.btvYear2);
year_views[2] = (BorderedTextView) findViewById(R.id.btvYear3);
// Acquire display size
display = getWindowManager().getDefaultDisplay();
size = new Point();
display.getSize(size);
int year_width = size.x / 2;
year_views[0].setWidth(year_width);
year_views[1].setWidth(year_width);
year_views[2].setWidth(year_width);
// This is done, because when scrolling, the third view which in the beginning is off-screen, could not be seen
RelativeLayout relLayout = (RelativeLayout) findViewById(R.id.rel_layout);
relLayout.getLayoutParams().width = year_width * 4;
relLayout.invalidate();
}
Then the onScroll-method:
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// intCurrYearMember is public, it stores the view that is next to be moved
// intRightYearMember; intCurrYearMember is located right to this view.
switch(intCurrYearMember) {
case 0:
intRightYearMember = 2;
case 1:
intRightYearMember = 0;
case 2:
intRightYearMember = 1;
}
// Move the views
for (TextView textview : year_views) {
textview.setX(textview.getX() - (distanceX / 2));
}
// Check if the view most left is now too far on left, and move it if needed to far right
if ((year_views[intCurrYearMember].getX() + year_views[intCurrYearMember].getWidth()) <= 0) {
// Is the problem here perhaps?
year_views[intCurrYearMember].setX(year_views[intRightYearMember].getRight());
intPreviousMember = intCurrYearMember;
if (intCurrYearMember < 2)
intCurrYearMember++;
else
intCurrYearMember = 0;
}
return true;
}
As it shows in the code, my idea is to build a year scroller. If someone happends to have a better, more efficient idea for how to do it, I am happy to hear your advices!
So my question is: why are there gaps between the textviews?

I would not suggest doing this at all. Utilizing Horizontal Swipes for these things is not a standard use of the platform and why waste your time developing this when there are already tested and pretty Android Components such as the Pickers that you can use easily.
A Horizontal Swiping gesture is normally saved for a Menu Drawer, View Pager, or other piece of functionality.

Related

Spinner dropdown items: how is width determined?

I have a Spinner in my app, with customized dropdown views. This is what the layout of the dropdown items look like:
<LinearLayout 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="wrap_content"
android:focusable="false"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatImageButton
android:id="#+id/leadingButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_weight="0" />
<FrameLayout
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_gravity="center_vertical"
android:layout_weight="1">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/dropdown_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/dropdown_text_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/dropdown_text" />
</RelativeLayout>
</FrameLayout>
<androidx.appcompat.widget.AppCompatImageButton
android:id="#+id/trailingButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_weight="0" />
</LinearLayout>
Android Studio warns me that my FrameLayout is useless. But when I take out the FrameLayout the dropdown views become narrow, and don't align with the spinner itself anymore. I have had the same problem when I tried to rewrite the drop-down items with a ConstraintLayout: the dropdown list became narrow, about half of the Spinner's size, and could not display all text, even though the ConstraintLayout had android:layout_width="match_parent".
A sketch to illustrate what I mean:
Why does this happen? How can I predict what the width of the dropdown menu will be based on the layout?
I find this dropdown view sizing quite magical
Did you look at the source code of the Spinner class? I just did. Here's what I found (API 27 Sources):
The spinner uses a ListView internally (first LOL), backed by DropdownPopup (private class):
private class DropdownPopup extends ListPopupWindow implements SpinnerPopup {
Before looking at it, look at ListPopupWindow because has a lot of info about the problems it has to deal with. It's a big class but among these things, you can see:
private int mDropDownHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
private int mDropDownWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
private int mDropDownHorizontalOffset;
private int mDropDownVerticalOffset;
It appears the DropDown is -by default- WRAPPING the content based upon the base class, however, the DropDownPopup that drives (and contains the adapter with all the items in the spinner) also has a void computeContentWidth() { method.
This method is called from the show() method, so before showing the popup, this computation happens every time.
I think here's part of the answer you're looking for:
void computeContentWidth() {
final Drawable background = getBackground();
int hOffset = 0;
if (background != null) {
background.getPadding(mTempRect);
hOffset = isLayoutRtl() ? mTempRect.right : -mTempRect.left;
} else {
mTempRect.left = mTempRect.right = 0;
}
final int spinnerPaddingLeft = Spinner.this.getPaddingLeft();
final int spinnerPaddingRight = Spinner.this.getPaddingRight();
final int spinnerWidth = Spinner.this.getWidth();
if (mDropDownWidth == WRAP_CONTENT) {
int contentWidth = measureContentWidth(
(SpinnerAdapter) mAdapter, getBackground());
final int contentWidthLimit = mContext.getResources()
.getDisplayMetrics().widthPixels - mTempRect.left - mTempRect.right;
if (contentWidth > contentWidthLimit) {
contentWidth = contentWidthLimit;
}
setContentWidth(Math.max(
contentWidth, spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight));
} else if (mDropDownWidth == MATCH_PARENT) {
setContentWidth(spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight);
} else {
setContentWidth(mDropDownWidth);
}
if (isLayoutRtl()) {
hOffset += spinnerWidth - spinnerPaddingRight - getWidth();
} else {
hOffset += spinnerPaddingLeft;
}
setHorizontalOffset(hOffset);
}
You may want to DEBUG and set breakpoints here to observe what these values are and what they mean.
The other piece there is the setContentWidth() method. This method is from the ListPopupWindow, and looks like:
/**
* Sets the width of the popup window by the size of its content. The final width may be
* larger to accommodate styled window dressing.
*
* #param width Desired width of content in pixels.
*/
public void setContentWidth(int width) {
Drawable popupBackground = mPopup.getBackground();
if (popupBackground != null) {
popupBackground.getPadding(mTempRect);
mDropDownWidth = mTempRect.left + mTempRect.right + width;
} else {
setWidth(width);
}
}
And setWidth (also in that class) all it does is:
/**
* Sets the width of the popup window in pixels. Can also be {#link #MATCH_PARENT}
* or {#link #WRAP_CONTENT}.
*
* #param width Width of the popup window.
*/
public void setWidth(int width) {
mDropDownWidth = width;
}
This mDropDownWidth seems used all over the place, but also made me found this other method in ListPopupWindow...
/**
* Sets the width of the popup window by the size of its content. The final width may be
* larger to accommodate styled window dressing.
*
* #param width Desired width of content in pixels.
*/
public void setContentWidth(int width) {
Drawable popupBackground = mPopup.getBackground();
if (popupBackground != null) {
popupBackground.getPadding(mTempRect);
mDropDownWidth = mTempRect.left + mTempRect.right + width;
} else {
setWidth(width);
}
}
So there you have it, more logic needed including the "window dressing" (?)
I agree the Spinner is a badly designed class (or rather, with outdated design) and even more so with the name (at this Google I/O in 2019, they actually explained in one of the sessions why the name "Spinner" hint: it comes from the 1st android prototypes). By looking at all this code, it would take a few hours to figure out what the spinner is trying to do and how it works, but the trip won't be pleasant.
Good luck.
I will reiterate my advice to use ConstraintLayout which you said you were familiar with; at the very least, discard weights.
By looking at how this works (ListView!!!) the weight calculation warrants a 2nd measure/layout pass, which is not only extremely inefficient and not needed, but also may be causing issues with the internal data adapter this DropDown thing manages so the "list" is displayed.
Ultimately, another class is also involved, this is all presented in a PopupView. PopupViews are what you see when you open a Menu item for example, and are very hard to customize sometimes, depending what you want to do.
Why Google chose this approach at the time, I don't know, but it certainly warrants an update and Material Design hasn't brought much to the table in this regard yet, as it will always be incomplete or in alpha state a year behind anything else.
It is telling you the FrameLayout is useless because it has a single child view ( the Relative Layout).
Your Framelayout has width defined as so:
<FrameLayout
android:layout_width="0dp"
android:layout_weight="1"
Your relative layout has its width defined as:
<RelativeLayout
android:layout_width="match_parent"
So just removing the FrameLayout means that a different "rule" is in place for the width.
To truly replace the FrameLayout with the RelativeLayout it should look like this:
<RelativeLayout
android:layout_width="0dp"
android:layout_weight="1"

Allow BottomSheet to slide up after threshold is reached on an area outside

I am trying to replicate a behavior that the current Google Maps has which allows the bottom sheet to be revealed when sliding up from the bottom bar.
Notice in the recording below that I first tap on one of the buttons at the bottom bar and then slide up, which in turn reveals the sheet behind it.
I cannot find anywhere explained how something like this can be achieved. I tried exploring the BottomSheetBehavior and customizing it, but nowhere I can find a way to track the initial tap and then let the sheet take over the movement once the touch slop threshold is reached.
How can I achieve this behavior without resorting to libraries? Or are there any official Google/Android views that allow this behavior between two sections (the navigation bar and bottom sheet)?
Took some time but I found a solution based on examples and discussion provided by two authors, their contributions can be found here:
https://gist.github.com/davidliu/c246a717f00494a6ad237a592a3cea4f
https://github.com/gavingt/BottomSheetTest
The basic logic is to handle touch events in onInterceptTouchEvent in a custom BottomSheetBehavior and check in a CoordinatorLayout if the given view (from now on named proxy view) is of interest for the rest of the touch delegation in isPointInChildBounds.
This can be adapted to use more than one proxy view if needed, the only change necessary for this is to make a proxy view list and iterate the list instead of using a single proxy view reference.
Below follows the code example of this implementation. Do note that this is only configured to handle vertical movements, if horizontal movements are necessary then adapt the code to your need.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<com.example.tabsheet.CustomCoordinatorLayout
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/customCoordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.tabs.TabLayout
android:id="#+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#android:color/darker_gray">
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="#drawable/ic_launcher_background"
android:text="Tab 1" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="#drawable/ic_launcher_background"
android:text="Tab 2" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="#drawable/ic_launcher_background"
android:text="Tab 3" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="#drawable/ic_launcher_background"
android:text="Tab 4" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="#drawable/ic_launcher_background"
android:text="Tab 5" />
</com.google.android.material.tabs.TabLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="#+id/bottomSheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#3F51B5"
android:clipToPadding="false"
app:behavior_peekHeight="0dp"
app:layout_behavior=".CustomBottomSheetBehavior" />
</com.example.tabsheet.CustomCoordinatorLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
final CustomCoordinatorLayout customCoordinatorLayout;
final CoordinatorLayout bottomSheet;
final TabLayout tabLayout;
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
customCoordinatorLayout = findViewById(R.id.customCoordinatorLayout);
bottomSheet = findViewById(R.id.bottomSheet);
tabLayout = findViewById(R.id.tabLayout);
iniList(bottomSheet);
customCoordinatorLayout.setProxyView(tabLayout);
}
private void iniList(final ViewGroup parent) {
#ColorInt int backgroundColor;
final int padding;
final int maxItems;
final float density;
final NestedScrollView nestedScrollView;
final LinearLayout linearLayout;
final ColorDrawable dividerDrawable;
int i;
TextView textView;
ViewGroup.LayoutParams layoutParams;
density = Resources.getSystem().getDisplayMetrics().density;
padding = (int) (20 * density);
maxItems = 50;
backgroundColor = ContextCompat.getColor(this, android.R.color.holo_blue_bright);
dividerDrawable = new ColorDrawable(Color.WHITE);
layoutParams = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
);
nestedScrollView = new NestedScrollView(this);
nestedScrollView.setLayoutParams(layoutParams);
nestedScrollView.setClipToPadding(false);
nestedScrollView.setBackgroundColor(backgroundColor);
linearLayout = new LinearLayout(this);
linearLayout.setLayoutParams(layoutParams);
linearLayout.setOrientation(LinearLayout.VERTICAL);
linearLayout.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
linearLayout.setDividerDrawable(dividerDrawable);
for (i = 0; i < maxItems; i++) {
textView = new TextView(this);
textView.setText("Item " + (1 + i));
textView.setPadding(padding, padding, padding, padding);
linearLayout.addView(textView, layoutParams);
}
nestedScrollView.addView(linearLayout);
parent.addView(nestedScrollView);
}
}
CustomCoordinatorLayout.java
public class CustomCoordinatorLayout extends CoordinatorLayout {
private View proxyView;
public CustomCoordinatorLayout(#NonNull Context context) {
super(context);
}
public CustomCoordinatorLayout(
#NonNull Context context,
#Nullable AttributeSet attrs
) {
super(context, attrs);
}
public CustomCoordinatorLayout(
#NonNull Context context,
#Nullable AttributeSet attrs,
int defStyleAttr
) {
super(context, attrs, defStyleAttr);
}
#Override
public boolean isPointInChildBounds(
#NonNull View child,
int x,
int y
) {
if (super.isPointInChildBounds(child, x, y)) {
return true;
}
// we want to intercept touch events if they are
// within the proxy view bounds, for this reason
// we instruct the coordinator layout to check
// if this is true and let the touch delegation
// respond to that result
if (proxyView != null) {
return super.isPointInChildBounds(proxyView, x, y);
}
return false;
}
// for this example we are only interested in intercepting
// touch events for a single view, if more are needed use
// a List<View> viewList instead and iterate in
// isPointInChildBounds
public void setProxyView(View proxyView) {
this.proxyView = proxyView;
}
}
CustomBottomSheetBehavior.java
public class CustomBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
// we'll use the device's touch slop value to find out when a tap
// becomes a scroll by checking how far the finger moved to be
// considered a scroll. if the finger moves more than the touch
// slop then it's a scroll, otherwise it is just a tap and we
// ignore the touch events
private int touchSlop;
private float initialY;
private boolean ignoreUntilClose;
public CustomBottomSheetBehavior(
#NonNull Context context,
#Nullable AttributeSet attrs
) {
super(context, attrs);
touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
#Override
public boolean onInterceptTouchEvent(
#NonNull CoordinatorLayout parent,
#NonNull V child,
#NonNull MotionEvent event
) {
// touch events are ignored if the bottom sheet is already
// open and we save that state for further processing
if (getState() == STATE_EXPANDED) {
ignoreUntilClose = true;
return super.onInterceptTouchEvent(parent, child, event);
}
switch (event.getAction()) {
// this is the first event we want to begin observing
// so we set the initial value for further processing
// as a positive value to make things easier
case MotionEvent.ACTION_DOWN:
initialY = Math.abs(event.getRawY());
return super.onInterceptTouchEvent(parent, child, event);
// if the last bottom sheet state was not open then
// we check if the current finger movement has exceed
// the touch slop in which case we return true to tell
// the system we are consuming the touch event
// otherwise we let the default handling behavior
// since we don't care about the direction of the
// movement we ensure its difference is a positive
// integer to simplify the condition check
case MotionEvent.ACTION_MOVE:
return !ignoreUntilClose
&& Math.abs(initialY - Math.abs(event.getRawY())) > touchSlop
|| super.onInterceptTouchEvent(parent, child, event);
// once the tap or movement is completed we reset
// the initial values to restore normal behavior
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
initialY = 0;
ignoreUntilClose = false;
return super.onInterceptTouchEvent(parent, child, event);
}
return super.onInterceptTouchEvent(parent, child, event);
}
}
Result with transparent status bar and navigation bar to help visualize the bottom sheet sliding up, but excluded from the code above since it was not relevant for this question.
Note: It is possible you might not even need a custom bottom sheet behavior if your bottom sheet layout contains a certain scrollable view type (NestedScrollView for example) that can be used as is by the CoordinatorLayout, so try without the custom bottom sheet behavior once your layout is ready since it will make this simpler.
You could try something like this (It's Pseudocode, hopefully you understand what I'm getting at):
<FrameLayout id="+id/bottomSheet">
<View id="exploreNearby bottomMargin="buttonContainerHeight/>
<LinearLayout>
<Button id="explore"/>
<Button id="explore"/>
<Button id="explore"/>
</LinearLayout>
<View width="match" height="match" id="+id/touchCatcher"
</FrameLayout>
Add a gesture detector on the bottomSheet view on override onTouch(). which uses SimpleOnGestureListener to wait for a "scroll" events - everything but a scroll event you can replicate down through to the view as normal.
On a scroll event you can grow your exploreNearby as a delta (make sure it doesn't recurse or go to high or too low).
The Bottom sheet class will already do this for you. Just set it's peek height to 0 and it should already listen for the slide up gesture.
However, I'm not positive it will work with a peek height of 0. So if that doesn't work, simply put a peek height of 20dp and make the top portion of the bottom sheet layout transparent so it is not visible.
That should do the trick for ya, unless I'm misunderstanding your question. If your goal is to simply be able to tap at the bottom and slide upwards bringing up the bottom sheet that should be pretty straight forward.
The one possible issue that you "could" encounter is if the bottom sheet doesn't receive the touch events due to the button already consuming it. If this happens you will need to create a touch handler for the whole screen and return "true" that you are handling it each time, then simply forward the touch events to the underlying view, so when you get above the threshold of your bottom tab bar you start sending the touch events to the bottom sheet layout instead of the tab bar.
It sounds harder than it is. Most classes have an onTouch and you just forward it on. However, only go that route, if it doesn't work for you out of the box the way I described in the first two scenarios.
Lastly, one other option that might work is to create your tab buttons as part of the bottomSheetLayout and make the peek height equivalent of the tab bar. Then make sure the tab bar is constrained to bottomsheet parent bottom, so that when you swipe up it simply stays at the bottom. This would enable you to click the buttons or get the free bottom sheet behavior.
Happy Coding!

Android Studio - Move View (Linear Layout)

it is almost 6 days traying to move the layout, but no success at all, trying alot of helps from internet but none helps..
I want llPic1 and llPic2 to be moved above ivNjeri with OnTouchListener.
So player to dicide which one need to be moved above ivNjeri.
With this code it vibrates on move and llPic1 and llPic2 goes under ivNjeri:
float dx = 0, dy = 0, x = 0, y = 0;
#Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
x = event.getX();
y = event.getY();
dx = x - view.getX();
dy = y - view.getY();
}
break;
case MotionEvent.ACTION_MOVE: {
view.setX(event.getX() - dx);
view.setY(event.getY() - dy);
}
break;
}
return true;
}
I'm also trying alot of other codes but none works, any help will be very very appriciated :)
You should use only 1 ViewGroup which holds all the images including an image to be overlapped and images to move.
Why image cannot go over the big image?
The small image on right side is inside of nested LinearLayout which is restricting the small image to be within the LinearLayout. That is why you can move the image inside of the child LinearLayout but not go beyond the boundary.
One example to fix it using RelativeLayout and fixed width on big image:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="#+id/bigimage"
android:layout_width="200dp"
android:layout_height="300dp"
android:src="#drawable/ic_launcher"/>
<ImageView
android:id="#+id/smallimage"
android:layout_width="match_parent"
android:layout_height="100dp"
android:src="#drawable/ic_launcher"
android:layout_toRightOf="#id/bigimage"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="100dp"
android:src="#drawable/ic_launcher"
android:layout_toRightOf="#id/bigimage"
android:layout_below="#id/smallimage"/>
</RelativeLayout>
If you want to keep the precise weight, check out
https://developer.android.com/reference/android/support/percent/PercentFrameLayout.html
https://developer.android.com/reference/android/support/percent/PercentRelativeLayout.html
This can give you weight control and can hold all views in 1 ViewGroup.
If you are trying only to animate view why you dont use Animation for it like this:
TranslateAnimation animation = new TranslateAnimation(0.0f, 400.0f,0.0f, 0.0f);
// new TranslateAnimation(xFrom,xTo, yFrom,yTo)
animation.setDuration(5000); // animation duration
animation.setRepeatCount(1); // animation repeat count
animation.setRepeatMode(1); // repeat animation (left to right, right to left )
//animation.setFillAfter(true);
llPic1.startAnimation(animation); // start animation
you can try this tutorial link. i think you need remove your pic 1 or pic2 (if you want disapeare after dragging.) and then you should add another image view on ivNjeri view

Small EditText have a setError with a lot of lines

I have a small EditText and I want to display errors (using editText.setError()) in it. In Android API 10 the message is displayed in a lot of lines and it's unreadable. In Android 15 works relatively fine. I attach screenshots to illustrate the problem at the end of the question.
How I can display the error messages in a appropriate mode?
I wrote a little example to reproduce the problem:
The Activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
((EditText) findViewById(R.id.b)).setError("A error description and bla bla bla bla bla.");
}
The layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<EditText
android:id="#+id/a"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1" />
<EditText
android:id="#+id/b"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1" />
<EditText
android:id="#+id/c"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1" />
<EditText
android:id="#+id/d"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1" />
<EditText
android:id="#+id/e"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1" />
<EditText
android:id="#+id/f"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>
Device with Android API 10:
Tablet with Android API 15:
Related question. But the answer doesn't work for me.
UPDATE
I executed the same code on two equals simulators except the API level. The results can be seen on the screens. The API 15 still does not fix the error completely. The text is legible but the popup is not in the correct position.
So looking at the source for 2.3.3 the width of the error text is set to slightly less than the width of the TextView it is related to.
They've jimmied around with that for 4.0.3 so that, in your example, the width of the pop-up is correct - but the nature of your layout is such that the pointer is in the wrong place.
I think you've a reasonable example for a bug report against 4.0.3 as I don't think you've got that unusual a use-case.
In order to sort this out though I'd recommend using a TextView that you hide and reveal as necessary. You can set an error image on the edit text as below.
Drawable errorImage = getContext().getResources().getDrawable( R.drawable.your_error_image);
theEditTextInQuestion.setCompoundDrawableWithIntrinsicBounds(errorImage, null, null, null);”
on api 14, TextView use this class;
private static class ErrorPopup extends PopupWindow {
private boolean mAbove = false;
private final TextView mView;
private int mPopupInlineErrorBackgroundId = 0;
private int mPopupInlineErrorAboveBackgroundId = 0;
ErrorPopup(TextView v, int width, int height) {
super(v, width, height);
mView = v;
// Make sure the TextView has a background set as it will be used the first time it is
// shown and positionned. Initialized with below background, which should have
// dimensions identical to the above version for this to work (and is more likely).
mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId,
com.android.internal.R.styleable.Theme_errorMessageBackground);
mView.setBackgroundResource(mPopupInlineErrorBackgroundId);
}
void fixDirection(boolean above) {
mAbove = above;
if (above) {
mPopupInlineErrorAboveBackgroundId =
getResourceId(mPopupInlineErrorAboveBackgroundId,
com.android.internal.R.styleable.Theme_errorMessageAboveBackground);
} else {
mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId,
com.android.internal.R.styleable.Theme_errorMessageBackground);
}
mView.setBackgroundResource(above ? mPopupInlineErrorAboveBackgroundId :
mPopupInlineErrorBackgroundId);
}
private int getResourceId(int currentId, int index) {
if (currentId == 0) {
TypedArray styledAttributes = mView.getContext().obtainStyledAttributes(
R.styleable.Theme);
currentId = styledAttributes.getResourceId(index, 0);
styledAttributes.recycle();
}
return currentId;
}
#Override
public void update(int x, int y, int w, int h, boolean force) {
super.update(x, y, w, h, force);
boolean above = isAboveAnchor();
if (above != mAbove) {
fixDirection(above);
}
}
}
And call like this;
final float scale = getResources().getDisplayMetrics().density;
mPopup = new ErrorPopup(err, (int) (200 * scale + 0.5f), (int) (50 * scale + 0.5f));
mPopup.setFocusable(false);
So Android TextView use your phone density. I tried for change density but i couldn't work because it need root. If you can this, maybe its work. But i guess it is not possible.
I think you should set the width of the edit text as fill parents so that it will take the proper place, otherwise give the width like 200 or 250 dp so that in that particular width your error message will be shown to you.
does those two emulator have the same screen sizes?.. if so, i think it would be appropriate to set the layout_widths and the heights to wrap content for the popup and if the error still persist then define specifically the width and height of the popup
I think a problem might be that the LinearLayout in your XML has no weightSum attribute. I do not know if you can use layout_weight in child views if LinearLayout does not have a weightSum declared. Also, try changing your layout_widths to "wrap_content" and get rid of the layout_weights if that doesnt work.

Can I increase a buttons onclick-area programmatically?

Sometimes I have a button in my UI that it is so small that it is difficult to click. My solution so far has been to add a transparent border around the button in photoshop. Just increasing the padding on the button does not work, since this will also stretch the image. Since it is kind of a fuss to open photoshop each time I want to change the clickable surface, is there any way to do this programmatically? I have tried placing a framelayout behind the button and make it clickable, but then the button wont change appearance on touch as it should. Ofcourse I could also add a ontouchlistener on the framelayout which changes the buttons appearance, but then it quite some code if I have several of those buttons.
Cheers,
Me personally, I'd use a TouchDelegate. This lets you deal with the touch target, and the visual view bounds as two different things. Very handy...
http://developer.android.com/reference/android/view/TouchDelegate.html
I have just found a neat way to solve this problem.
Surround the button with a say a LinearLayout that has the padding round the button.
Add the same onclick to the LinearLayout as the Button.
In the Button set the duplicateParentState to true which make the button highlight when you click outside the button but inside the LinearLayout.
<LinearLayout
android:layout_height="fill_parent"
android:onClick="searchButtonClicked"
android:layout_centerVertical="true"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:paddingRight="10dp"
android:paddingLeft="30dp">
<Button
android:id="#+id/search_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/toggle_button_selector"
android:textColor="#fff"
android:text="Search"
android:focusable="true"
android:textStyle="bold"
android:onClick="searchButtonClicked"
android:layout_gravity="center_vertical"
android:duplicateParentState="true"/>
</LinearLayout>
This is a very late "me too," but after coming to this and other questions looking for a solution, I found a simple, elegant solution of my own.
Another question complained that the transparent background of their image was not clickable. If that is an issue, this seems to get around that as well.
Here's the button:
<ImageButton
android:id="#+id/arrowUp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/arrow_up"
android:background="#drawable/clear_button_background" />
The relevant lines are the last two. "#drawable/arrow_up" is a few button states in *.png files defined in a drawable *.xml file like this:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="true"
android:drawable="#drawable/tri_up_blue" /> <!-- pressed -->
<item android:state_selected="true"
android:drawable="#drawable/tri_up_blue" /> <!-- selected -->
<item android:state_focused="true"
android:drawable="#drawable/tri_up_blue" /> <!-- focused -->
<item android:drawable="#drawable/tri_up_green" /> <!-- default -->
</selector>
Just your basic button. Nothing special. And "#drawable/clear_button_background" is just this:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#android:color/transparent"/>
<size android:width="20dp" android:height="30dp" />
</shape>
The height and width here are the clickable area, resize as needed. You can reuse this for as many buttons as you need in a single view, unlike the absurdly detailed TouchDelegate. No additional listeners. It doesn't add any views or groups to your hierarchy and you won't be messing around with padding and margins all day.
Simple. Elegant.
I think your solution is the best one available at the moment, if you don't want to go deep into some android stuff and intercept all the motionEvent and TouchEvents yourself and then you also would need to trigger the pressed view of the button yourself etc.
Just create a nine patch image with a stretchable transparent border. In that way you can change the size of the image without the need to change the image itself and your button will grow or shrink without the actual displayed background changing.
Anothe idea is to make the new transparent image and put icon on it so the touch area will be more and design look perfect. Check out the image
Thank you.
Simply provide padding to the layout in place of Margin
<ImageView
android:id="#+id/back_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="25dip"
android:src="#drawable/back_btn" />
What I did is not the recommended way and very few will find it useful.
For me it was the best solution, because TouchDelegate did not worked in all View hierarchy scenarios.
The solution is based on overriding the hidden
View class
public boolean pointInView(float localX, float localY, float slop)
Here is the code:
public boolean pointInView(float localX, float localY, float slop) {
boolean res = ae_pointInView(localX, localY, slop, slop);
if(!res) {
if(viewIsClickableButToSmall(this)) {
//our view is clickable itself
float newSlopX = Math.max(slop, slop + minTouchableViewWidth - this.getWidth());
float newSlopY = Math.max(slop, slop + minTouchableViewHeight - this.getHeight());
if(ae_pointInView(localX, localY, newSlopX, newSlopY)) {
return true;
}
}
//the point is outside our view, now check for views with increased tap area that may extent beyond it
int childCount = getChildCount();
for(int i = 0; i < childCount;i++) {
View child = getChildAt(i);
if(child instanceof MyViewGroup) {
//this is our container that may also contain views with increased tap area
float[] newPoint = ae_transformPointToViewLocal(localX, localY, child);
if(((MyViewGroup) child).pointInView(newPoint[0], newPoint[1], slop)) {
return true;
}
}
else {
if(viewIsClickableButToSmall(child)) {
float[] newPoint = ae_transformPointToViewLocal(localX, localY, child);
float newSlopX = Math.max(slop, slop + minTouchableViewWidth - this.getWidth());
float newSlopY = Math.max(slop, slop + minTouchableViewHeight - this.getHeight());
if(ae_pointInView(newPoint[0], newPoint[1], newSlopX, newSlopY)) {
return true;
}
}
}
}
return false;
}
else
return true;
}
private int minTouchableViewHeight, minTouchableViewWidth;
private boolean viewIsClickableButToSmall(View view)
{
minTouchableViewHeight = GlobalHelper.dipToDevicePixels(40);
minTouchableViewWidth = GlobalHelper.dipToDevicePixels(40);
return (view.getHeight() < minTouchableViewHeight || view.getWidth() < minTouchableViewWidth) && view.hasOnClickListeners();
}
public boolean ae_pointInView(float localX, float localY, float slopX, float slopY) {
boolean res = localX >= -slopX && localY >= -slopY && localX < ((getRight() - getLeft()) + slopX) &&
localY < ((getBottom() - getTop()) + slopY);
return res;
}
private float[] tempPoint;
public float[] ae_transformPointToViewLocal(float x, float y, View child) {
if(tempPoint == null) {
tempPoint = new float[2];
}
tempPoint[0] = x + getScrollX() - child.getLeft();
tempPoint[1] = y + getScrollY() - child.getTop();
return tempPoint;
}
Maybe you could do so by looking at the X and Y coordinates of the MotionEvent passed into onTouchEvent ?

Categories

Resources