I have a custom floorplanView class within my main Activity. This custom view uses up roughly half the screen space. I am trying to simply create a star image and place it on the floor plan onTouch. However, for some reason this appears to place the image below where I am touching (I have tried a few variations and cannot work it out).
public class FloorplanView extends RelativeLayout {
public FloorplanView(Context context, AttributeSet attrs) {
super(context, attrs);
SCROLL_THRESHOLD = getResources().getDimensionPixelSize(R.dimen.margin5);
setClipChildren(false);
setOnTouchListener(new FloorplanTouchListener());
}
public class FloorplanTouchListener implements View.OnTouchListener {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
posX = event.getRawX() - v.getX();
posY = event.getRawY() - v.getY();
break;
case MotionEvent.ACTION_UP:
ImageView img = new ImageView(getContext());
img.setOnTouchListener(new FlagTouchListener());
img.setBackground(getResources().getDrawable(android.R.drawable.btn_star));
img.setX(event.getRawX()); //Used posX/Y without success
img.setY(event.getRawY());
addView(img);
break;
}
return true;
}
}
}
Further to this, is there a way to set bounds to my custom class with extends a relativeLayout so that when I drag and move my image (which is setup and working) it does not go outside of the Floorplanview custom class.
EDIT:
The parent Activity class XML
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.fcs_systems.inspector.FloorplanActivity">
<TextView
android:id="#+id/txtFloorplanName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Floorplan Name"
android:visibility="gone"/>
<com.fcs_systems.inspector.FloorplanView
android:id="#+id/floorplanView"
android:background="#color/fcs_red"
android:layout_width="match_parent"
android:layout_height="200dp"/>
</RelativeLayout>
According to the documentation,
float getRawX () : Returns the original raw X coordinate of this event. For touch events on the screen, this is the original location of the event on the screen, before it had been adjusted for the containing window and views.
(Source : https://developer.android.com/reference/android/view/MotionEvent.html#getRawX() )
This is why getRawX() and getRawY() seem to place the image lower than where you tapped.
You should simply use the getX() and getY() methods on the MotionEvent instead.
As for your images going out of the RelativeLayout, this will depends on two things :
Add your images to your RelativeLayout (not sure what your actual addView(img); does)
Make sure your RelativeLayout doesn't have https://developer.android.com/reference/android/view/ViewGroup.html#setClipChildren(boolean) enabled
This will make sure that your images belong to your RelativeLayout container, and will not be drawn when moved outside of it.
Related
I'm currently working on an app that allows users to add a relative layout, linear layout, or nested linear layout within a linear layout, which is a child of a scroll view. The scrolling seems to work fine when just the relative layout or linear layout is added, however, once the nested linear layout is added, the scrolling becomes a problem. Basically, I want the scroll view to start scrolling once my dragged view comes within x amount of the top/bottom of the screen. Any advice? Below is some of my code to help
protected class myDragEventListener implements View.OnDragListener {
// This is the method that the system calls when it dispatches a drag event to the listener.
public boolean onDrag(View v, DragEvent event) {
// Defines a variable to store the action type for the incoming event
final int action = event.getAction();
Point touchPosition = getTouchPositionFromDragEvent(v, event);
Log.d("y", String.format("%s", String.format("%s", eventTouchYCoord)));
//View dragView = (View) event.getLocalState();
// Handles each of the expected events
switch(action) {
case DragEvent.ACTION_DRAG_STARTED:
return true;
case DragEvent.ACTION_DRAG_ENTERED:
return true;
case DragEvent.ACTION_DRAG_LOCATION:
Log.d("point", touchPosition.toString());
if (touchPosition.y > (absoluteBottom + mScrollDistance - 350)){
Log.d("bottom", "greater");
Log.d("actionbar", String.format("%s", actionBarHeight()));
homeScroll.scrollBy(0, 30);
}
if (touchPosition.y < (actionBarHeight + 350)){
Log.d("top", "greater");
homeScroll.scrollBy(0, -30);
}
break;
getTouchPositionFromDragEvent Method
public static Point getTouchPositionFromDragEvent(View item, DragEvent event) {
Rect rItem = new Rect();
item.getGlobalVisibleRect(rItem);
return new Point(rItem.left + Math.round(event.getX()), rItem.top + Math.round(event.getY()));
}
onScrollChangedListener Method
homeScroll.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
mScrollDistance = homeScroll.getScrollY();
int[] coords = new int[2];
homeScroll.getLocationOnScreen(coords);
absoluteTop = coords[1];
absoluteBottom = coords[1] + homeScroll.getHeight();
}
});
Its always best practice to implement scrolling attribute in xml file. This that you want scroll in side it. or in Palette you will find scrollview option of vertical and horizontal scrolling.. just drag and drop to where you want place it.
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ScrollView>
I am currently creating an Android app where someone can input their name, press a button, and then it just outputs their name back to them.
One effect that I would like to achieve with this is an effect where, after they push the button, the input and button will vanish (complete this bit so far), and then the background colour of the MainActivity's view will do a ripple (from the centre) with a new colour, eventually changing the full background colour.
How would I go about doing this programatically, since I am only able to find tutorials on adding ripples to buttons when pushed?
EDIT:
I tested this by making a small app
First of all hide the view you want to reveal in this animation.
The view can be from the same layout and in xml its visibility should be invisible so that the animation will reveal it.
You can set the view height and width to match parent if you want to create a full screen animation...
Take your original and reveal view both in frame layout
In my case,I have used this:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:text="Hello World!"
android:layout_width="wrap_content"
android:textSize="20sp"
android:layout_height="wrap_content" />
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimaryDark"
android:id="#+id/revealiew"
android:visibility="invisible"
>
</FrameLayout>
then in your activity on button click or some event do this:
fab.setOnClickListener(new View.OnClickListener() {
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
#Override
public void onClick(View view) {
// previously invisible view
View myView = findViewById(R.id.revealview);
// get the center for the clipping circle
int cx = myView.getWidth() / 2;
int cy = myView.getHeight() / 2;
// get the final radius for the clipping circle
int finalRadius = Math.max(myView.getWidth(), myView.getHeight());
// create the animator for this view (the start radius is zero)
Animator anim =
ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
//Interpolator for giving effect to animation
anim.setInterpolator(new AccelerateDecelerateInterpolator());
// Duration of the animation
anim.setDuration(1000);
// make the view visible and start the animation
myView.setVisibility(View.VISIBLE);
anim.start();
}
});
}
You can take detailed look at official documentation here:
http://developer.android.com/training/material/animations.html
What you are describing is a reveal effect on the background.
From the official doc you can find ready to use examples:
1) Here is how to reveal a previously invisible view using reveal effect:
// previously invisible view
View myView = findViewById(R.id.my_view);
// get the center for the clipping circle
int cx = myView.getWidth() / 2;
int cy = myView.getHeight() / 2;
// get the final radius for the clipping circle
int finalRadius = Math.max(myView.getWidth(), myView.getHeight());
// create the animator for this view (the start radius is zero)
Animator anim =
ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
// make the view visible and start the animation
myView.setVisibility(View.VISIBLE);
anim.start();
2) Here is how to hide a previously visible view using the reveal effect:
// previously visible view
final View myView = findViewById(R.id.my_view);
// get the center for the clipping circle
int cx = myView.getWidth() / 2;
int cy = myView.getHeight() / 2;
// get the initial radius for the clipping circle
int initialRadius = myView.getWidth();
// create the animation (the final radius is zero)
Animator anim =
ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0);
// make the view invisible when the animation is done
anim.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
myView.setVisibility(View.INVISIBLE);
}
});
// start the animation
anim.start();
In you app, you can use a colored background layer (invisible at the beginning) and then use the reveal effect on it.
check this site, "Android Ripple Background" is a library to do it and the min sdk is 11 (Android 3.0 Honeycomb) https://android-arsenal.com/details/1/1107
Essentially, I am re-asking this question but for implementing it on android.
I am trying to allow users to swipe between filters on a static image.
The idea is that the image stays in place while the filter scrolls
above it. Snapchat recently released a version which implements this
feature. This video shows exactly what I'm trying to accomplish at
1:05.
I tried filling a list with the overlays and paging through it with the onFling and drawing with onDraw, but I lose the animations. Is there a way this can be done with ViewPager?
EDIT: As requested, I have provided my implementation for overlay view paging. It fills the viewpager with transparent png images which sits on top of an image view. Also, this code is in C#, as I am using Xamarin Android. It's fairly similar to Java for those unfamiliar with C#
...
static List<ImageView> overlayList = new List<ImageView>();
...
public class OverlayFragmentAdapter : FragmentPagerAdapter
{
public OverlayFragmentAdapter(Android.Support.V4.App.FragmentManager fm) : base(fm)
{
}
public override int Count
{
get { return 5; } //hardcoded temporarily
}
public override Android.Support.V4.App.Fragment GetItem(int position)
{
return new OverlayFragment ();
}
}
public class OverlayFragment : Android.Support.V4.App.Fragment
{
public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.Inflate (Resource.Layout.fragment_overlay, container, false);
LinearLayout l1 = view.FindViewById<LinearLayout> (Resource.Id.overlay_container);
ImageView im = new ImageView (Activity);
im.SetImageResource (Resource.Drawable.Overlay); //Resource.Drawable.Overlay is a simple png transparency I created. R
l1.AddView (im);
overlayList.AddElement (im);
return view;
}
}
Activity Layout XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="bottom">
<ImageView
android:id="#+id/background_image"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<RelativeLayout <!-- This second layout is for buttons which I have omitted from this code -->
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="#+id/edit_layout">
<android.support.v4.view.ViewPager
android:id="#+id/overlay_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
</RelativeLayout>
Fragment Overlay XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/overlay_container"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center" />
To briefly summarize: the viewpager sits on top of the first imageview, which acts as a background. The OnCreateView method creates an overlay fragment and an overlay imageview from a resource, which it puts inside the overlay_container layout. Saving the image (Which I have not posted as it is outside the scope of this question) is simple, all it does is create a background bitmap, an overlay bitmap, and uses a canvas to draw the overlay onto the background, then writes to file.
I've worked on something similar myself.
For your specific use case, I would just use a canvas and alpha blend the filters across, on fling, as the top image.
To do the alpha blending, set the alpha paint of the first image (the original) to 255 and the alpha of the second one (the filter) to something like 128.
You just need a filter with the size of the image and then you shift the position of the second image as you draw it. That's it.
It's extremely fast and works a treat on very, very old devices.
Here's a sample implementation:
Bitmap filter, // the filter
original, // our original
tempBitmap; // the bitmap which holds the canvas results
// and is then drawn to the imageView
Canvas mCanvas; // our canvas
int x = 0; // The x coordinate of the filter. This variable will be manipulated
// in either onFling or onScroll.
void draw() {
// clear canvas
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
// setup paint
paint0.setAlpha(255); // the original needs to be fully visible
paint1.setAlpha(128); // the filter should be alpha blended into the original.
// enable AA for paint
// filter image
paint1.setAntiAlias(true);
paint1.setFlags(Paint.ANTI_ALIAS_FLAG); // Apply AA to the image. Optional.
paint1.setFlags(Paint.FILTER_BITMAP_FLAG); // In case you scale your image, apple
// bilinear filtering. Optional.
// original image
paint0.setAntiAlias(true);
paint0.setFlags(Paint.ANTI_ALIAS_FLAG);
paint0.setFlags(Paint.FILTER_BITMAP_FLAG);
// draw onto the canvas
mCanvas.save();
mCanvas.drawBitmap(original, 0,0,paint0);
mCanvas.drawBitmap(filter, x,0,paint1);
mCanvas.restore();
// set the new image
imageView.setImageDrawable(new BitmapDrawable(getResources(), tempBitmap));
}
And here are basic onFling and onScroll implementations.
private static final int SWIPE_DISTANCE_THRESHOLD = 125;
private static final int SWIPE_VELOCITY_THRESHOLD = 75;
// make sure to have implemented GestureDetector.OnGestureListener for these to work.
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
float distanceX = e2.getX() - e1.getX();
float distanceY = e2.getY() - e1.getY();
if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) >
SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
// change picture to
if (distanceX > 0) {
// start left increment
}
else { // the left
// start right increment
}
}
}
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// checks if we're touching for more than 2f. I like to have this implemented, to prevent
// jerky image motion, when not really moving my finger, but still touching. Optional.
if (Math.abs(distanceY) > 2 || Math.abs(distanceX) > 2) {
if(Math.abs(distanceX) > Math.abs(distanceY)) {
// move the filter left or right
}
}
}
Note: The onScroll/onFling implementations have pseudo code for the x adjustments, as those functions need to be tested. Someone who ends up implementing this in the future, can feel free to edit the answer and provide those functions.
Take a look in the implementation of the method onDraw for the default Calendar app: DayView.
There is onFling implementation and redrawing of the content (for example, calendar grid) according to the motion changes, which imitates fling.
Then you can use ColorFilter in onDraw according to the motion changes. It is very fast.
Alternatively, you can use ViewSwitcher with a list of filtered images (or somehow created a filtered images cache). To achieve the possibility of "drawing over the image", you can use ImageView and ViewSwitcher in RelativeLayout one above another and set the new filtered image in ImageView after the end of scrolling.
For this application i feel it would be most easy to use androids animation features and set the animations value to the filter you want. So you would make your own animation the changed filters iterating over your array.
Currently, I'm trying to make an infinitely long chain of ImageViews that are all next to each other.
I attempt to do this by using addRule and LayoutParam so that every new ImageView has to be set to the right of the ImageView before it.
However, I encounter an odd problem. When I do run my code, this is the result :
Not only are my two ImageViews not even next to each other, it doesn't work for any number past two! If I just create two blocks, it gives the result below.
If I make any more blocks than 2, they'll just stack on top of block #2. Which is what happens in the image above.
Also, I have to set every ImageView with its own ID because if I don't, they'll all have an ID of -1.
I've been stuck on this for like 5 hours in a row, and it's really starting to get annoying. (Please do not suggest that I use a LinearLayout. I already know, and I can't use it.)
public class TerrainFactory {
int count = 0;
int idCount = 1;
Terrain terrain;
public Terrain createNewTerrain(Activity activity, RelativeLayout relativeLayout,
final ArrayList<Terrain> terrainArrayList){
//TODO: Add the rectangle bounds into this.
terrain = new Terrain();
terrain.is = activity.getResources().openRawResource(+R.drawable.game_tile);
terrain.tile = BitmapFactory.decodeStream(terrain.is);
terrain.tileDrawable = new BitmapDrawable(activity.getResources(), terrain.tile);
terrain.terrainImage = new ImageView(activity);
terrain.terrainImage.setImageDrawable(terrain.tileDrawable);
//noinspection ResourceType
terrain.terrainImage.setId(idCount);
idCount++;
if(count >= 1) {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);;
layoutParams.addRule(RelativeLayout.RIGHT_OF, terrainArrayList.get(count - 1).terrainImage.getId());
relativeLayout.addView(terrain.terrainImage, layoutParams);
terrain.terrainImage.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
//TODO: Set rectangle bounds in here
++count;
terrain.terrainImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}else{
++count;
relativeLayout.addView(terrain.terrainImage);
terrain.terrainImage.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
//TODO: Set rectangle bounds in here
terrain.terrainImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}
return terrain;
}
public int getCount() { return count; }
}
Here is my PlayActivity class, where all the block creating happens :
public class PlayActivity extends Activity {
RelativeLayout relativeLayout;
TerrainFactory terrainFactory;
ArrayList<Terrain> terrainArrayList = new ArrayList<Terrain>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play);
relativeLayout = (RelativeLayout) findViewById(R.id.relativeLayout);
terrainFactory = new TerrainFactory();
for(int i = 0; i < 7; i++){
terrainArrayList.add(terrainFactory.createNewTerrain(PlayActivity.this, relativeLayout, terrainArrayList));
}
}
}
And here is the Terrain class
public class Terrain {
public Bitmap tile;
public InputStream is;
public ObjectAnimator moveLeft;
public Drawable tileDrawable;
public Rect bounds;
public ImageView terrainImage;
}
Here's the XML for the RelativeLayout. The RelativeLayout I'm using is the one nested inside the RelativeLayout that fills the entire screen.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hooray.project.fun.activity.PlayActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="false"
android:id="#+id/relativeLayout"></RelativeLayout>
</RelativeLayout>
Basically I jsut would tackle this in another way....using not Views for buildiung terrains but using surfaceview and draw bitmaps on it...
But well lets do it your way:
Place your views into a FrameLayout. Then when you create your views give them their dimension in code and also set the x and y positions in code.
For example this is how I give a View the needed Dimension and the position in my Code. Its a Textview and I jsut set the x and y position which I have stuffed iinto variables so I can chnage them how i want:
tView.setLayoutParams(new FrameLayout.LayoutParams(hintListWidth,hintListHeight));
tView.setX(hintListPosX);
tView.setY(hintListPosY);
maybe for your example do it like this:
float startPosX=???;
float startPosY=???;
float i=0;
for(Terrian t:terrainArrayList)
{
tView.setLayoutParams(new FrameLayout.LayoutParams(width,height));
tView.setX(startPosX+width*i);
tView.setY(startPosY);
i++;
}
If you have the width and height ready and know where to start placing the first view every view will be placed side by side from left to right...
As suggested in the other answer(s) to your question, the problem is that counter on the if-true branch is never incremented before views are drawn (aka tree observer callback being called). You are calling addView but I think this goes on and adds the view asynchronously, while you go on and add views. So you finish adding the views and only after that they are drawn. I suggest taking the counter out of the callback and place it at the end of the if-true block.
Note: if you are targeting SDK17+ you can use View.generateViewId() instead of manually keeping track / incrementing it yourself. Not that it matters in this particular scenario, but it will not allow the generated id to conflict with aapt-generated ids and it will roll it over once the top limit is reached.
In an android application I am implementing an Animation on an ImageButton. After the animation the button remains at it's last frame but it is only clickable at it's initial position, I have Implemented an BounceInterpolation animation by these line of code in my main .java Activity
TranslateAnimation translation;
translation = new TranslateAnimation(0f, 0F, 0f, 200);
translation.setStartOffset(150);
translation.setDuration(100);
translation.setFillAfter(true);
translation.setInterpolator(new BounceInterpolator());
mTourButton.startAnimation(translation);
I don't know how to update the the ImageButton parameters. Most of the solutions are for the xml implemented animations. I didn't find any solution. I am tired now please help.
Try to use onTouch() to handle your clicks.
imageButton.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction())
{
case MotionEvent.ACTION_UP :
{
// Do whatever you want here.
}
return true;
}
});
That should do it.
Better add onClick in XML:
<ImageButton
android:onClick="doSomethingMethod"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/imageButton"
android:layout_above="#+id/imageView"
android:layout_centerHorizontal="true" />
Press ALT+ENTER on doSomethingMethod and Create 'doSomethingMethod(View)' in 'Activity'
This will create a method like onClickListener in your Activity.
Hope it helps!