I made a custom background for a button and also for different button states. But now I made to a point that I cannot understand.
When button is in normal state then it looks just fine. But when I press the button, I need to move text down few pixels because button background image moving (actually it feels like it moving on the image, because first there's border under the button and when it's in pressed state then this border disappears). Please see image below.
How can I move the buttons text in the button when buttons state is pressed? (maybe padding somehow or layout custom for a button)
Setting padding in 9-patch didn't work for me.
Setting padding in touch listeners is messy, would litter code anywhere buttons are used.
I went with subclassing Button, and it turned out reasonably tidy. In my case, I wanted to offset icon (drawableLeft) and text 1px left and 1px down.
Subclassed button widget:
package com.myapp.widgets;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.Button;
import com.myapp.R;
public class OffsetButton extends Button {
public OffsetButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public OffsetButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public OffsetButton(Context context) {
super(context);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
boolean value = super.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_UP) {
setBackgroundResource(R.drawable.btn_normal);
} else if (event.getAction() == MotionEvent.ACTION_DOWN) {
setBackgroundResource(R.drawable.btn_pressed);
setPadding(getPaddingLeft() + 1, getPaddingTop() + 1, getPaddingRight() - 1,
getPaddingBottom() - 1);
}
return value;
}
}
And use it in layout like this:
<com.myapp.widgets.OffsetButton
android:text="#string/click_me"
android:drawableLeft="#drawable/icon_will_be_offset_too_thats_good" />
Few notes:
I did not use StateListDrawable for background, and am instead switching backgrounds in code. When I tried using StateListDrawable, there would be small pause between padding change and background change. That didn't look good.
Setting background resets padding, so don't need to adjust padding in ACTION_UP case
It was important to increase top and left padding, and at the same time decrease bottom and right padding. So the size of content area stays the same and content area is effectively just shifted.
I did not try it myself but if you use nine-patch as a background drawable for both states then you should consider setting proper padding box in pressed state drawable. See details here.
You can use padding on the view. You can add an OnTouchListener to the button or view like
viewF.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction == MotionEvent.ACTION_UP) {
//set your padding
} else if (event.getAction == MotionEvent.ACTION_DOWN){
//set your padding
}
return true;
}
});
The ontouchlistener will let your know when the button is pressed and not.
The Pēteris Caune answer works worse than overriding setPressed(). But everithing OK with setPressed until you test your app at ICS or lower device and add button as listview item.
To archieve this I've improved my button's class:
public class OffsetButton extends Button {
private static final int OFFSET_IN_DP = 6;
private int offset_in_px;
private boolean wasPressed = false;
private Integer[] defaultPaddings;
public OffsetButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
public OffsetButton(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public OffsetButton(Context context) {
super(context);
initView();
}
private void initView() {
offset_in_px = (int) DisplayUtil.convertDpToPixel(OFFSET_IN_DP);
}
#Override
public void setPressed(boolean pressed) {
if (pressed && !wasPressed) {
changePaddings();
}
if (!pressed && wasPressed) {
resetPaddings();
}
super.setPressed(pressed);
}
private void changePaddings() {
defaultPaddings = new Integer[]{getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom()};
setPadding(getPaddingLeft(), getPaddingTop() + offset_in_px, getPaddingRight(), getPaddingBottom() - offset_in_px);
wasPressed = true;
}
private void resetPaddings() {
setPadding(defaultPaddings[0], defaultPaddings[1], defaultPaddings[2], defaultPaddings[3]);
wasPressed = false;
}
#Override
public boolean performClick() {
resetPaddings();
return super.performClick();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (isEnabled())
{
if (event.getAction() == MotionEvent.ACTION_DOWN && !wasPressed) {
changePaddings();
} else if (event.getAction() == MotionEvent.ACTION_UP && wasPressed) {
resetPaddings();
}
}
return super.onTouchEvent(event);
}
}
this might just work:
setPadding(left, top, right, bottom); // Normal
setPadding(left, top + x, right, bottom - x); // Pressed
Related
So, I was going to create a custom view in order to get a photo album like tinder, a scrollview with autoadjust at photos positions. I thought an horizontalscrollview with a couple of tweaks would do the job. Added a 5*LayoutWidth horizontal LinerLayout to the scrollView sw and defined its onTouchListener like this:
sw.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("MotionEvent", String.valueOf( event.getAction()));
if(event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL){
int scrollX=sw.getScrollX();
Log.d("scrollX", String.valueOf( scrollX));
//Log.d("layoutWidth", String.valueOf( layoutWidth));
int aux = Math.round((float)scrollX/layoutWidth)*layoutWidth;
Log.d("correction", String.valueOf( aux));
sw.setScrollX(aux );
}
return false;
}
});
It is working just as expected, focusing on one of the 5 photos when releasing the touch input , but when I scroll and relese by fast gestures the scrollview is just ignoring sw.setScrollX(aux) and keeps scrolling. Can I do something to avoid this automatic scroll after release¿ Can I somehow manipulate the data of the input event¿
Thanks!
Finally I got the solution. I extended the HorizontalcrollView like that:
public class stepScrollView extends HorizontalScrollView {
public stepScrollView(Context context) {
super(context);
}
public stepScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public stepScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
Display d = ((Activity) getContext()).getWindowManager().getDefaultDisplay();
Point size = new Point();
d.getSize(size);
int layoutWidth = size.x;
int aux = Math.round((float)this.getScrollX()/layoutWidth)*layoutWidth;
this.setScrollX(aux );
return false;
default:
return super.onTouchEvent(ev);
}
}
}
it works with all the images you want to put in a Horizontal LinearLayout inner this custom view. You also have to resize your images to the stepScrollView horizontal dimension. In my case layoutWidth.
What I am doing is, I am creating Edit Text Views on click of a button. What i want is, as soon as, a Edit Text View appears a small button will also gets appears to its right(having (X) image(so, that i can delete that particular Edit Text View). I am able to generate Edit Text Views but have no idea regarding the button creation.Please guide or help me.
You can simply create a new button the same way as the EditText view with an x in it an align it to the right of the EditText field, and then do an onclicklistener on the button that will call to the db or whatever you are using to remove the actual text and then you can remove both the view and edit button from the layout. However, from the code you provided I do not see anything in regards to a onClickListener being implemented.
Here is my implementation
<CustomEditText
android:id="#+id/edittext1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawableRight="#drawable/ic_action_cancel"
android:ems="10"
android:focusable="true"
android:singleLine="true"
android:textColor="#000" />
where ic_action_cancel is x icon.
and following is the custom Edittext class
public class CustomEditText extends EditText {
private Drawable drawableRight;
int actionX, actionY;
public CustomEditText(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public CustomEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomEditText(Context context) {
super(context);
}
#Override
public void setCompoundDrawables(Drawable left, Drawable top,
Drawable right, Drawable bottom) {
if (right != null) {
drawableRight = right;
}
super.setCompoundDrawables(left, top, right, bottom);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
Rect bounds;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
actionX = (int) event.getX();
actionY = (int) event.getY();
// this works for left since container shares 0,0 origin with bounds
if (drawableRight != null) {
bounds = null;
bounds = drawableRight.getBounds();
int x, y;
int extraTapArea = 13;
int extraTapArea = 20;
x = (int) (actionX + extraTapArea);
y = (int) (actionY - extraTapArea);
x = getWidth() - x;
if (x <= 0) {
x += extraTapArea;
}
if (y <= 0)
y = actionY;
if (bounds.contains(x, y)) {
setText("");
return false;
}
return super.onTouchEvent(event);
}
}
return super.onTouchEvent(event);
}
}
I'm trying to create a button-click animation, e.g. the button scales down a little when you press it, scales back up when you release. If you simply tap, you get the press and release strung together.
I set up an onTouchListener and a couple XML-defined AnimatorSets, one for the press and one for the release. Ran the press on ACTION_DOWN, the release on ACTION_UP or ACTION_CANCEL. This works fine when you press and hold the button, then release a little later. But with a quick tap, the release animation triggers before the press one is done, and often the result is no animation at all.
I'd hoped I could use AnimatorSet's sequential capabilities to stick the release animation onto the end of the possibly-already-running press animation, but no luck. I'm sure I could rig something up with callbacks, but that seems messy.
What's the best approach here? Thanks!
I built a sample Button class that buffers animations and executes them in a queue according to what you need:
public class AnimationButton extends Button
{
private List<Animation> mAnimationBuffer = new ArrayList<Animation>();;
private boolean mIsAnimating;
public AnimationButton(Context context)
{
super(context);
}
public AnimationButton(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public AnimationButton(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
generateAnimation(1, 0.75f);
triggerNextAnimation();
}
else if (event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP)
{
generateAnimation(0.75f, 1);
triggerNextAnimation();
}
return super.onTouchEvent(event);
}
private void generateAnimation(float from, float to)
{
ScaleAnimation scaleAnimation = new ScaleAnimation(from, to, from, to);
scaleAnimation.setFillAfter(true);
scaleAnimation.setDuration(500);
scaleAnimation.setAnimationListener(new ScaleAnimation.AnimationListener()
{
#Override
public void onAnimationStart(Animation animation) { }
#Override
public void onAnimationRepeat(Animation animation) { }
#Override
public void onAnimationEnd(Animation animation)
{
mIsAnimating = false;
triggerNextAnimation();
}
});
mAnimationBuffer.add(scaleAnimation);
}
private void triggerNextAnimation()
{
if (mAnimationBuffer.size() > 0 && !mIsAnimating)
{
mIsAnimating = true;
Animation currAnimation = mAnimationBuffer.get(0);
mAnimationBuffer.remove(0);
startAnimation(currAnimation);
}
}
}
Hope this helps :)
Looks like L Preview adds the ability to specify animations for state changes in the xml file.
https://developer.android.com/preview/material/animations.html#viewstate
I have implemented an application with image view in my application i would like to implement when user move the image right side then i would like to get the next acitvity.
How can i write an event or any other code for move the image just right side?
I have used am imageView code as:
(ImageView)findViewById(R.id.slide) //event for move right here
Intent it = new Intent(Slide.this,NextActivity.class);
startActivity(it);
Please any body help me...
So, if i understood you correctly, you want to move an ImageView across the screen, drag it, and when you hit a certain spot with it you want to start another activity?
If so, you could try and use the code below...which let's you drag your imageview across the screen
Just add an onTouchListener to the imageView you're trying to slide (move/drag)
yourSlideImageViewObject.setOnTouchListener(new View.OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
onSlideTouch(v, event);
return false;
}
});
Then add this code to handle the Touch events
public void onSlideTouch( View view, MotionEvent event )
{
//When the user pushes down on an ImageView
if ( event.getAction() == MotionEvent.ACTION_DOWN )
{
inDragMode = true; //Set a variable so we know we started draggin the imageView
//Set the selected ImageView X and Y exact position
selectedImageViewX = Math.abs((int)event.getRawX()-((ImageView)view).getLeft());
selectedImageViewY = Math.abs((int)event.getRawY()-((ImageView)view).getTop());
//Bring the imageView in front
((ImageView)view).bringToFront();
}
//When the user let's the ImageView go (raises finger)
if ( event.getAction() == MotionEvent.ACTION_UP )
{
inDragMode = false; //Reset the variable which let's us know we're not in drag mode anymore
}
//When the user keeps his finger on te screen and drags it (slides it)
if ( event.getAction() == MotionEvent.ACTION_MOVE )
{
//If we've started draggin the imageView
if ( inDragMode )
{
//Get the imageView object
ImageView slide = (ImageView)findViewById(R.id.slide);
//Get a parameters object (THIS EXAMPLE IS FOR A RELATIVE LAYOUT)
RelativeLayout.LayoutParams params = RelativeLayout.LayoutParams)slide.getLayoutParams();
//Change the position of the imageview accordingly
params.setMargins((int)event.getRawX()-selectedImageViewX, (int)event.getRawY()-selectedImageViewY, 0, 0);
//Set the new params
slide.setLayoutParams(params);
//If we hit a limit with our imageView position
if( slideImageView_position is inside an interval )
{
//Open another activity
Intent it = new Intent(Slide.this,NextActivity.class);
startActivity(it);
}
}
}
}
The above should drag your imageview accross the screen.
You can also implement in the MotionEvent.ACTION_MOVE event .... to limit the movement of the ImageView accross the screen. So check the new coordinates of your finger across the screen and check if they are out of bounds. If so, then do nothing. If they aren't out of bounds, then change the left and upper margins of the ImaveView.
Also, on the MotionEvent.ACTION_UP event, check to see if the left margin of the ImaveView is over a predefined limit (which means the user slided the imageView over you intended position). If so, you can continue doing whatever you want, which means starting an activity. OR, if you don't want to open the activity while the user is dragging, not only after he has removed his finger from the imageView, then just do that if () i've written under the MotionEvent.ACTION_MOVE event. Insert there the interval you want, so when the imageView is dragged inside that interval, the activity starts.
If you're confused, please ask :) It's very late here, and i'm tired, so i might have not explained it properly :D
I don't quite understand your question, but I interpret it as that you want to slide an ImageView containing Activity into another Activity.
If that is the case, I would actually convert those Activities to being Fragments and then use a ViewPager. Here is a good Google Developer blog post that introduces this concept: http://android-developers.blogspot.com/2011/08/horizontal-view-swiping-with-viewpager.html
You'll want to implement a FragmentPagerAdapter with getCount being the number of pages the user can swipe between. Then getItem should return a Fragment containing the first ImageView for position 0 and a Fragment containing what is in your NextActivity for position 1. You can return more Fragments for positions 2 and higher to have an interface that you can swipe between, left and right. This hopefully gives you the sliding user experience that you are looking for.
package avatar.view.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
public class customview extends View {
private int PanX, PanY;
private GestureDetector gestureDetector;
private Bitmap mBitmap;
private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
public customview(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO 自動生成されたコンストラクター・スタブ
}
public customview(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO 自動生成されたコンストラクター・スタブs
}
public customview(Context context) {
super(context);
// TODO 自動生成されたコンストラクター・スタブ
}
private float pointX = 0, pointY = 0;
private void setPoint(MotionEvent e) {
pointX = e.getX();
pointY = e.getY();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mBitmap != null) {
pointX = PanX - (mBitmap.getWidth() / 2);
pointY = PanY - (mBitmap.getHeight() / 2);
canvas.drawBitmap(mBitmap, pointX, pointY, mPaint);
}
}
/*
* (非 Javadoc)
*
* #see android.view.View#onMeasure(int, int)
*/
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// ビューのサイズを設定する
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec
.getSize(heightMeasureSpec));
}
public void setPanX(int panX) {
PanX = panX;
}
public int getPanX() {
return PanX;
}
public void setPanY(int panY) {
PanY = panY;
}
public int getPanY() {
return PanY;
}
public void setmBitmap(Bitmap mBitmap) {
this.mBitmap = mBitmap;
}
public Bitmap getmBitmap() {
return mBitmap;
}
}
Custom a view . Then in Main Activity
#Override
public boolean onTouch(View v, MotionEvent e) {
// TODO 自動生成されたメソッド・スタブ
if (isSelected == false) {
return false;
}
final int action = e.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
panX = (int) e.getX();
panY = (int) e.getY();
ctview.setPanX(panX);
ctview.setPanY(panY);
ctview.invalidate();
Log.v("ACTION_DOWN", "ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
panX = (int) e.getX();
panY = (int) e.getY();
ctview.setPanX(panX);
ctview.setPanY(panY);
ctview.invalidate();
Log.v("ACTION_MOVE", "ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.v("ACTION_UP", "ACTION_UP");
if (isSelected) {
isSelected = false;
ctview.setmBitmap(null);
ctview.invalidate();
}
break;
default:
break;
}
return true;
}
And Oncreate
Customview.setmBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.ImageSlide);
I have a custom ImageView('CustomImageView') and an Edit-text in a linear layout of an activity 'ImageViewActivity'. The Edit-text is initially set invisible. When the customimageview is touched, and onDraw() is called, I want the visibility of the Edit-text to be set visible. Where should I put the code for this?
Code for ImageViewActivity:
public class ImageViewActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.custom_imageview);
}
}
And the code for CustomImageView is:
public class CustomImageView extends ImageView {
Paint paint = new Paint();
float xp = -1, yp = -1;
private Options opt;
public CustomImageView(Context context) {
super(context);
init();
}
public CustomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public void init() {
opt = new BitmapFactory.Options();
opt.inJustDecodeBounds = true;
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
xp = event.getX();
yp = event.getY();
invalidate();
}
return true;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//some code
setMeasuredDimension(width, height);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (xp >= 0 && yp > 0) {
canvas.drawCircle(xp, yp, 20, paint);
}
}
}
Implement touch listener on your customImageview class and then call the edittext to hide it or show is as you need, for results of touch on custom view you have to define touchlistener on your customview not of activity and you can manage views of parent activity in touchevent of customview OR other method is implement touch listener on you activity and find the view which is touched if touched view is your custom view then make visible your Edittext. Then you will not need to do anything in onDraw() as regarding the edittext.
I'd recommend setting the view to visible once you have detected a touch motion on your particular view. Specifically it doesn't matter how the user touches it, since once they touch it you want to show your view (at least that is what you said you wanted).