Start an activity with a shared element from custom view - java

I want to create a animation between two Activities with a image as shared element, see Customize Activity Transitions
My problem: In the source activity the image is drawn on a canvas of a custom view :-(
Is there a way to use this image as a shared element or do I have to add a real ImageView?

You can't share the image only, but you can share the entire custom view. This means that the entire custom View would disappear from the calling Activity when the shared element is transferred to the launched Activity. If your custom View only has the image, that would be fine, but if it paints other things, that would be disastrous.
If you want to share only the image, you'll have to create a View (e.g. ImageView) and move the image to it and then share it. That way, when the shared element is transferred, it hides properly from the calling activity.
The shared elements don't actually move Views between Activities, they just share a 'snapshot' of the view as a bitmap and the position of the view. In the launched activity, the view with the given transition name will be laid out in that position. You can use the snapshot or not, depending on your needs. By default, the snapshot is not used.
So you'll need some code like this:
public void launchActivity(final Intent intent, final CustomView view) {
final Bitmap bitmap = view.getSharedImage();
ImageView imageView = new ImageView(view.getContext());
imageView.setBitmap(bitmap);
LayoutParams layoutParams = view.createSharedImageLayoutParams();
final ViewGroup parent = (ViewGroup)view.getParent();
parent.addView(imageView, layoutParams);
parent.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
#Override
public boolean onPreDraw() {
parent.getViewTreeObserver().removeOnPreDrawListener(this);
customView.hideSharedImage();
ActivityOptions activityOptions = ActivityOptions.
makeSceneTransitionAnimation(this, imageView, "destName");
startActivity(intent, activityOptions.toBundle();
}
});
setExitSharedElementCallback(new SharedElementCallback() {
#Override
public void onSharedElementsArrived(List<String> sharedElementNames,
List<View> sharedElements, OnSharedElementsReadyListener listener) {
super.onSharedElementsArrived(sharedElementNames, sharedElements,
listener);
parent.removeView(imageView);
customView.showSharedImage();
}
});
}
I haven't specifically tried the above, but that's the essence of it. If you don't want to use the newer onSharedElementsArrived, you can create a custom ImageView that listens for onVisibilityChanged. If you have an exit transition, you can also listen for the end of it as well. You just need some trigger that will tell you to reset the state so that the ImageView is removed and your custom View should draw the image again.
In the example above, I placed the ImageView in the same parent as the custom View. You may get more flexibility by putting it into the DecorView, but you'll have to figure out what the global position is and it will also overlay everything on the screen. Alternatively, since I added the ImageView to the parent, that won't work for all parents (e.g. ListView, LinearLayout). You know your View hierarchy and you'll have to choose the best place to put it.
Or, you could change your custom View to be a custom ViewGroup and contain the sharable image as an ImageView! Sounds easier to me.

Related

Android - setVisibility not changing visibility of RelativeLayout

I am trying to manipulate the visibility of RelativeLayout on a certain click event, using the visibility attribute.
After adding event handlers to each list item, I can check that the visibility status is changing, but in the Android emulator, the screen doesn't change at all (and I have tested it with visibility="visible" in the XML to make sure that it would show up).
Here's the code for the click handler:
someHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
LayoutInflater layoutSeed = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View baseView = layoutSeed.inflate(R.layout.activity_listings, null, false);
RelativeLayout popup = (RelativeLayout) baseView.findViewById(R.id.popupContainer);
Log.d("Tag1",Integer.toString(popup.getVisibility()));
popup.setVisibility(View.VISIBLE);
Log.d("Tag2",Integer.toString(popup.getVisibility()));
}
});
Logcat shows the status change. I've also tried to invalidate() and postInvalidate() on both baseView and popup, as well as popup.bringToFront() and the combinations of these and so far nothing's working.
Any suggestions or possible routes to investigate?
If the RelativeLayout you are trying to change visibility is already in the screen, you don't need to inflate it again. Please make a reference to already inflated one and change its visibility
holder.relativeLayout.setVisibility(View.GONE);
Gone will remove the view from your layout. Use Invisible instead of gone.
when you click on button your View is replace by button's OnClick(View v) method.
so you just write down
v.popup(View.GONE)
Look at this line of code:
View baseView = layoutSeed.inflate(R.layout.activity_listings, null, false);
RelativeLayout popup = (RelativeLayout) baseView.findViewById(R.id.popupContainer);
Log.d("Tag1",Integer.toString(popup.getVisibility()));
Actually, you're trying to create a new baseView and set it gone. If you want to set the row item gone, you should remove it from your adapter temporary instead such as (if you set itemView to gone, you should be careful with the resuing view of the adapter):
removeItemAt(getAdapterPosition())
notifyDataRemoved(getAdapterPosition())

ImageView hiding and showing

I'm struggling to get my android app to show and hide imageView objects programmatically. Actually I'm struggling to get expected behavior from imageView objects full stop.
Following the answers to this question,
Here's what I'm testing with:
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback{
#Override
protected void onCreate(Bundle savedInstanceState) {
ImageView warn = (ImageView)findViewById(R.id.gpsStatusWarning);
warn.setImageResource(R.drawable.gps_error);
warn.getLayoutParams().height = 64;
warn.getLayoutParams().width = 64;
}
}
The above code is called in the parent activity's OnCreate method, and it does exactly what I expect: It changes the image of the object to be the one designated, and it sets the height and width of said object. However, what I can't seem to do is to set the object as INVISIBLE or GONE. I just can't make it vanish at all, in fact. I've tried both:
warn.setVisibility(View.INVISIBLE);
warn.setVisibility(View.GONE);
But the image is still visible. I've even tried changing it in the XML to
android:visibility="gone"
But even that hasn't helped. The image is still visible.
What am I doing wrong? Am I missing a call to some update method? Does setting the image resource force the image to be drawn?
Try :
warn.setImageResource(0);
Or : warn.setImageResource(android.R.color.transparent);
This is how you set the visibility of ImageView objects on Android programatically:
yourImage.setVisibility(View.VISIBLE);
yourImage.setVisibility(View.INVISIBLE);
yourImage.setVisibility(View.GONE);
You can also set the initial states of ImageView objects in XML layout files like this:
visibility="visible"
visibility="gone"
visibility="invisible"
You can follow the official documentation about ImageView controls to try it on yourself on this link below. Learn how to set the visibility state of a view.
imageView.setVisibility(View.GONE);
imageView.setVisibility(View.VISIBLE);

Passing a Layout and it's contents between Activities

So full admission, I am a bit self taught when it comes to Android Dev, so I maybe going about this all wrong. As such I am open to suggestions! I'm essentially trying to semi-automate a task I do every day currently.
Question: How to Pass a LinearLayout, it's contents intact, between Activities?
So I have this 2nd Activity, called reportGeneratorActivity In this activity there is a Linear Layout directly under the Report Preview.
The Linear Layout itself is defined in a separate XML file as previewplate.xml
Now this Activity functions, when you put text into the upper fields it updates the preview below. Which brings me to the brick wall I'm hitting. The Goal is to take that preview plate and add it to my main activity that I've named rootActivity in the white area which is a Linear Layout itself named rootWorkingLayout.
Now the Strings from the text are all stored temporarily in reportGeneratorActivity at which point I am doing this when the button is pressed:
public void beginReport (View view) {
//Bundle the Preview
Bundle previewBundle = new Bundle();//Create the Bundle
previewBundle.putString("date", dateHolder);
previewBundle.putString("client", clientNameHolder);
previewBundle.putString("machine", machineTypeHolder);
previewBundle.putString("serial#", serialNumberHolder);
previewBundle.putString("notes", notesHolder);
// Prepare The Intent
Intent previewPasser = new Intent(this, rootActivity.class);
previewPasser.putExtras(previewBundle); // Add the Bundle to Intent
//Send Preview to Root
startActivity(previewPasser);
//Send Preview to History
//Send User to Decision Tree
}
From what I understand, I've put all the strings in Bundle previewBundle, then attached the bundle to the previewPasser intent and sent the intent back to rootActivity.
In rootActivity, within the onCreate function I have placed this code:
Bundle previewReceiver = getIntent().getExtras();
//If There is a Bundle, Process it
if(previewReceiver != null) {
newPreview(previewReceiver);
}
The Goal here is to grab the Intent, and grab the bundle then pass it to my newPreview function (currently empty) that will duplicate finished preview from report_generator_activity and desplaying within the Linear Layout: rootWorkinglayout in an identical fashion.
It's this final step that I am hitting a brick wall on, I can only assume there is an easier way, perhaps a way to duplicate the Layout and it's contents and send it over? Or if I am doing this functionally, How do I unpack the data in an identical manner?
Please forgive the verbosity and lack of images as I am a new member of the community.
Edit #1:
In response to SoroushA's excellent answer that has put me on the correct path, I've adjusted my newPreview Method to be this:
public void newPreview (Bundle previewReceiver) {
//Extract Strings from Bundle
String date = previewReceiver.getString("date");
String client = previewReceiver.getString("client");
String machine =previewReceiver.getString("machine");
String serialNum = previewReceiver.getString("serial#");
//Create New Inflater
LayoutInflater previewInflater = (LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View previewLayout = previewInflater.inflate(R.layout.previewplate, null);
//Add previewLayout to rootWorkingLayout
rootWorkingLayout.addView(previewLayout);
}
Currently, I am just trying to get the grey box of the preview plate layout to appear as it's background is defined in it's own XML file. However, nothing is occurring when I go through the process clearly due to my own error. I am unsure of what step I am missing.
Thanks in advance!
I hope that I understood your question clearly.
In your newPreview method, start by getting the Strings back from the Bundle:
public void newPreview(Bundle preview){
String dateHolder = preview.getString("date");
//similarly for other strings
}
Then inflate the LinearLayout and start setting its elements.
LayoutInflater inflater = (LayoutInflater)context.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.your_xml, null);
//call findviewbyid on the layout and set its children using the strings you extracted
How to Pass a LinearLayout, it's contents intact, between Activities?
You don't. Widgets and containers (i.e., subclasses of View) are owned by their activity.
I can only assume there is an easier way
Have only one activity, altering its UI as needed (e.g., use fragments and replace them as needed). These seem to be way too closely coupled to be two separate activities.
The Goal here is to grab the Intent, and grab the bundle then pass it to my newPreview function (currently empty) that will duplicate finished preview from report_generator_activity and desplaying within the Linear Layout: rootWorkinglayout in an identical fashion.
There is nothing intrinsically wrong with this approach. You act as though you are having problems implementing it ("How do I unpack the data in an identical manner"), but we do not have nearly enough information on which to provide you with much advice. In general, a Bundle has getter methods, to retrieve that values that you put into the Bundle via the setter methods.

How would I go about creating a custom container view in Android?

I would like to create a view with a listener that acts sort of like a container or grocery bag. Any image or text that is dragged into it, gets collected and saved into a local database. Or rather the image name or string gets saved. I've created custom views before that displayed a custom row of data but I'm not sure how to create a "grocery bag" type of view. I've searched Google for creating a custom container view with listener but couldn't find anything related to what I am looking for. I'm not asking anyone to do it for me, just give me some advice or push in the right direction.
Edit
Just to clarify a little more. I already know how to drag and drop a view. The problem with that is that you can drop anywhere. What I want is a view that when something is dropped within its bounds, it gets the views' string or tag. Regardless of what kind of view that may be. Somehow this custom view has to know what kind of view was dropped within its bounds and remove that view on drop.
<view class="at.calista.quatscha.views.SwipebarLayout"
android:id="#+id/sbl"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<!-- Top View -->
<Button android:text="Top" android:id="#+id/Button01" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<!-- Bottom View -->
<Button android:text="Bottom" android:id="#+id/Button02" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
You would need a method that determines whether two views overlap. For example, something like these methods could work for when the target view and the dragged item view are 2 distinct views in the same container view:
private Rect getScreenBounds(View view)
{
int[] location = new int[2];
view.getLocationOnScreen(location);
return new Rect(location[0], location[1], location[0] + view.getWidth(), location[1] + view.getHeight());
}
private boolean doViewsIntersect(View dropTarget, View item)
{
Rect dropRect = getScreenBounds(dropTarget);
Rect itemRect = getScreenBounds(item);
return Rect.intersects(dropRect, itemRect);
}
However, if the views being dragged are child views on a container, and that container has a special hit area where you want to perform the drop logic, then you can just use that hit area's Rect for the intersect test.
To save information on the View, you have a few options:
You can use view.setTag(key, object) and view.getTag(key) to store whatever information you'd like on each item.
If you are using a custom class for the items, you can just add a method to get the data from the view class.
If you are using multiple different classes for the items, you could make an interface, and implement that interface on each draggable view class.
On an item's release, you could then check whether the areas/views intersect, and if they do, get whatever information you need from the dragged item to process as necessary, either using getTag or the method added to the view.
For example, this could be triggered via an onTouchListener, when you receive a MotionEvent.ACTION_UP event. Here's an example of a listener that you can add on each item:
item.setOnTouchListener(new View.OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
if (event.getActionMasked() == MotionEvent.ACTION_UP)
{
if (doViewsIntesect(dropTarget, v))
{
Object data = v.getTag(DATA_KEY);
//process data
((ViewGroup) v.getParent()).removeView(v); // to remove the item afterwards
}
}
return false;
}
});

Android - RecyclerView with one layout, multiple setVisibility

I have a basically all in one layout which has everything needed for my app's main feed. All variable items (images, video thumbnails.. Etc.) are set to GONE at first and set to VISIBLE when it is needed.
The problem is sometimes, might be due to RecyclerView's recycling behavior, the item which is supposedto be GONE is VISIBLE in the wrong places.
Example :
Item no 1 contains Text
Item no 2 contains Image
Item no 3 contains Image
I keep scrolling down to item no x, then scroll back up and here's what I get :
Item no 1 contains Image from item no x, sometimes item no 3
Item no 2 contains Image
Item no 3 contains Image
I'm using a custom ViewHolder which extends RecyclerView.ViewHolder.
The purpose of the CustomViewHolder is for layout declaration and initialization.
ProgressBar progressBar;
View viewDimmer;
RelativeLayout postListWrapper;
...
public ObjectViewHolder(View v) {
super(v);
progressBar = (ProgressBar)v.findViewById(R.id.post_inscroll_progressBar);
viewDimmer = (View)v.findViewById(R.id.post_inscroll_viewDimmer);
postListWrapper = (RelativeLayout)v.findViewById(R.id.post_inscroll_postListWrapper);
}
An example of how I load the image :
Picasso.with(context)
.load(youtubeThumbnailUrl)
.fit()
.centerCrop()
.into(
((ObjectViewHolder) holder).userPostYoutubeImage
);
I've set each visibility to GONE if no url is obtained from the server
((ObjectViewHolder) holder).userPostImageWrapper.setVisibility(View.GONE);
((ObjectViewHolder) holder).userPostYoutubeImageWrapper.setVisibility(View.GONE);
But somehow the image is still reused on the previous items (yes, not only Item no 1). Sometimes image are also in the wrong ImageView. Image D is supposed to be in ImageView D, but it's in ImageView A instead.
Any guide for setting RecyclerView up and going nicely?
If I miss anything, or need to supply more code, please do inform me :D
You need to put the else condition too. Like the example below.
// if no url is found from server
if(url == null){
((ObjectViewHolder) holder).userPostImageWrapper.setVisibility(View.GONE);
((ObjectViewHolder) holder).userPostYoutubeImageWrapper.setVisibility(View.GONE);
} else {
// Some url has found
((ObjectViewHolder) holder).userPostImageWrapper.setVisibility(View.VISIBLE);
((ObjectViewHolder) holder).userPostYoutubeImageWrapper.setVisibility(View.VISIBLE);
}
Do this for each of the items you've got there as the list item in case of you're setting their visibilities in runtime.
All your if conditions in onBindViewHolder() must have an else block too.
Don't leave any if condition without else. You can provide a default behaviour in the else block when if condition becomes false.

Categories