Android: how can I add different layer to a image view dynamically? - android

I have one fragment layout with a background image. Programmatically I have to add the different layer to this image because I want to add some particular details depending of some data. There are three different details that it can change in 3 different way. ( there is a thread that works at 5/10hz and it loads the right details)
The following is my first implementation. In this version, I have done all of works in XML layout and in a programmatically way, when the user clicks on a certain button, I set the visibility of each image view. I have seen that is much expensive, and sometimes the inflate layout return null:
<RelativeLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/background"
>
<ImageView
android:id="#+id/layer1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:src="#drawable/ballgreen"/>
<ImageView
android:id="#+id/layer2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:src="#drawable/ballred"/>
<ImageView
android:id="#+id/layer3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:src="#drawable/ballyellow"/>
... 6 more image view layer....
</RelativeLayout>
Then I thought to use an async task to decode all drawable image, make a bitmap fusion and in a post execute load the result bitmap in a single image view.
But the asynctask is not the better way to implement, because the user can click on that button many times and each time I have to call new asynctask ..
Are there a clever ways to implement all of this?
Goodbye and thanks a lot :)
EDIT : each layer is a detail that thread can add to that image.
For example : a car, a tree, a red ball ecc ecc. In the xml example for me each imageview is a layer, and this is wrong. But I don't know the clever way to implement ..
EDIT2 : Moreover I know the maximum amount of layer, but there are many combinations. there are 3 point and for each point I can choice between 3 differents details
Edit3: i add some details , there is a thread that change laYer and not the user

Use the LayerDrawable class, adding new layers as the user enables them. I don't think you can remove layers from it, but you can create a new LayerDrawable each time the user disables a layer. Or you can use setDrawable(int, Drawable) to replace a layer.
This means you don't have to complicate your layouts, and should get rid of those pesky extra inflation/measure/layout passes.
As for the image loading / async task stuff: use an LRU cache for your bitmaps. When a layer is enabled, check the cache, and if it's there, just use it. Otherwise, use an async task to load it.

Related

android click image to zoom in (larger than screen) and move around, and click again to go back to default size

I've done my searches extensively for sure, but I haven't come across exactly what I'm looking for. As such, I haven't any code to show for what I want to do. I want to know if it's possible, first of all, and then get a lead on how to implement such an action.
To keep my app simple, I'd really like to avoid starting a new activity, which seems to be involved in many of the "solutions" that I've seen.
My setup isn't anything special, just a layout with some textviews, buttons, and an image that's about as large as the screen.
I want the user to be able to click the image, and the image will "zoom" or "enlarge" to about 4-5x the size, obviously much of the image will then be off screen. Then, the user should be able to drag the image around to view the entire image as they wish. When they're done looking at the image, they should be able to click to go back to the original size.
I'm sure this will warrant a separate image for each size (one for the original, and a larger more detailed image for the enlarged version). Though, the simpler the better. If I can maintain one larger detailed image and scale it down for the standard view before it's clicked, that'd be great.
I'm assuming they'll be a click listener on the image, but I'm not sure how to do what I want without starting a new activity entirely, then upon clicking again, going back to the original activity. Can it be done in the same activity?
Thanks in advance.
EDIT:
I just had an idea. I could have the larger image in a relative view and make it invisible/gone, then make it visible upon click of the original/main image. Clicking the large image will make itself invisible/gone again. But, assuming this works, I do'nt konw how to set the action of dragging it around and boundaries to make sure you can't drag it outside of the screen entirely.
i have use this library in my project for Zooming images and its very simple to use its helps you ...
https://github.com/chrisbanes/PhotoView
JAVA:
PhotoView mImageView;
PhotoViewAttacher mAttacher;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Any implementation of ImageView can be used!
mImageView = (PhotoView) findViewById(R.id.iv_photo);
// Set the Drawable displayed
Drawable bitmap = getResources().getDrawable(R.drawable.wallpaper);
mImageView.setImageDrawable(bitmap);
// Attach a PhotoViewAttacher, which takes care of all of the zooming functionality.
mAttacher = new PhotoViewAttacher(mImageView);
}
// If you later call mImageView.setImageDrawable/setImageBitmap/setImageResource/etc then you just need to call
attacher.update();
XML:
<uk.co.senab.photoview.PhotoView
android:id="#+id/iv_photo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp" />
In hopes that this helps someone else and that my solution will suffice in fixing someone else's issue, I'm going to answer my own question with what I found to be a good fix.
To keep it as simple as possible (as it seems that implementing such a feature requires more extensive work that I feel should be necessary), I used the method of toggling hidden/invisible views.
<FrameLayout>
<!--Main Activity-->
<LinearLayout>
<!--Primary Image-->
<ImageView
android:onClick="makeZoomVisible"/>
</LinearLayout>
<!--ScrollView Holder for Enlarged Image-->
<HorizontalScrollView
android:visibility="gone">
<!--Enlarged Image-->
<ImageView
android:onClick="makeZoomInvisible"/>
</HorizontalScrollView>
</FrameLayout>
The base layout is a Frame layout, which allows me to place the Enlarged Image holder (horizontal scroll view) on top of everything else since it comes after the main activity view (linear layout).
Once the primary image is clicked, it calls the method "makeZoomVisible", which as you may guess, toggles the visibility for the horizontal scroll view which holds the enlarged image. The image is scaled up to as large as the height of the screen allows. This works for me because my app is portrait only, and allows the image to scale up approximately 4x the size (a 2x2 of the original image size, just for comparison). Since the image maxes out at the height of the screen, there's no need to scroll/drag up and down. But the horizontal scroll allows the image to be moved side to side to view the whole image.
Clicking the enlarged image calls the method "makeZoomInvisible" which toggles its own visibility, clearing it away and allowing the user to see the main layout of the activity again.
This is the best option for me as a new Android app developer because of its simplicity. To refrain from switching activities, saving/pausing the current activity, implementing 3rd party libraries to include a horizonal+vertical scroll, or pinch zoom and drag features I'm able to achieve what I need for my app.
Hopefully this helps someone else out that may be looking for a simple solution.
I know you decided to implement simple transition yourself without using 3rd party libs. But if someone else is looking for more complex solution, I can suggest to try this library, it allows zooming from i.e. RecyclerView items to ViewPager with animation and includes zoom/pan/rotation features as well.
(Note, I'm the developer of this library)
Simple usage example:
Add regular ImageView and GestureImageView into layout:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/image_small"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.alexvasilkov.gestures.views.GestureImageView
android:id="#+id/image_full"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Set same image into both views:
smallImage.setImageDrawable(...);
fullImage.setImageDrawable(...);
Start animation:
fullImage.getPositionAnimator().enter(smallImage, true);
For more info see documentation and sample project.
A great way to do this would be to use a custom ImageView to handle the zooming and dragging of the image that way you can keep it in the same layout without having to start a new activity.
You should check out the TouchImageView library. It contains a custom ImageView that allows pinch zoom, double tap to zoom, dragging, and the ability to manually set the zoom of the image.
If don't want to include the full library a simple way to add this functionality would be to copy the TouchImageView java class into a package in your app (eg com.myapp.views).
Then in your xml layout instead of using:
<ImageView
android:id="#+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/my_image" />
Change your image to use the path of the custom view class:
<com.myapp.views.TouchImageView
android:id="#+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/my_image" />

change the background color of the application from a fragment

I am currently using an architecture where I have multiple fragments that I swap in the same activity.
I need to be able to use a different background color for one of these fragments BUT I don't want to use the lazy solution that adds one layer of overdraw.
For clarity : I have a window background setted by my theme and on top of it I draw cards & lists. In one of my fragments I need a slightly different window background color.
Is there any way to do that ? I already tried to use a ContextThemeWrapper but it does not seem to work (maybe because the background has already been drawed ?)
Thanks in advance for your help.
Just found it :
It is doable by calling
ColorDrawable cd = new ColorDrawable(getActivity().getResources().getColor(
R.color.your_color));
getActivity().getWindow().setBackgroundDrawable(cd);
during the initialization of your fragment, when it is attached to the activity.
Unfortunately changing a fragment's background at runtime isn't supported. To get this functionality you can wrap your fragment inside of a RelativeLayout or something similar and set the background programmatically on that.
<RelativeLayout
android:id="#+id/fragmentContainer1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ccc">
<fragment android:name="whatever.fragment.name"
android:id="#+id/frag_1"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</RelativeLayout>
Then in your code get by id fragmentContainer1 and do view.setBackgroundColor(YourColorVariable) on it.
Another way may be to get your fragment, find the root view, then set the background on that. fragment.getRootView().setBackgroundColor(Color.WHITE) This depends on your fragment's layout though, so it may or may not work.

Multiple views on top of an ImageView

I'm trying to port an app from iOS to Android. In my iOS app I have an image view which displays a map, and on top of it I want to display multiple button views on certain specific positions.
None of the standard views in Android seem to fit my needs, but I am surely missing something. How could I achieve this?
Thanks
The easiest thing to do would be to use any layout that's best suited for your needs (LinearLayout, FrameLayout, RelativeLayout, etc.) and set its background to any image you need. I believe the image will automatically scale to fill the corresponding Layout. Put the image into the appropriate drawable folders - and format your XML similar to this:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/buttons"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#drawable/background_image">
...
</RelativeLayout>

How to use LayoutInflater / ViewStub for an overlay

As I am actually not very confident with programatically changing Views, I have following problem:
At the first start of my app, I want to have an overlay for the main screen, that tells the user to have a look at the settings, as there are two critical options the user has to configure.
I don't want to use an AlertDialog and rather not use a wizard. So, I decided to take an approach similar to Go SMS and create an overlay at the first start. The mockup I created looks like this:
Normal menu:
First start:
So these are the problems I have:
Like I said, I don't want to use a screenshot overlaying on first start, as this would take too much space and would not be language and screen independent.
I would have the circle as an png, but I don't know how exactly put it over the image
The same problem with the text
And finally I want to put a semi-transparent white over the app. It does not necessarily need the hole for the icon, though it would be nice.
In case you need the Layout Source, you can get it at pastebin
So, I just need to get a start here, if it is better to use LayoutInflater or ViewStub and how to realize it, as I have absolutely no experience with it...
Thanks!
/edit: I uploaded a new, more well-arranged layout.
I have faced a similar problem, I client wanted a walkthrough of the application, where the entire screen had to become whiter (as they said: "transparent"), except for the button being explained by an overlay speech-bubble.
Fortunately for you, your layout is not nearly as complicated as the one I had to work with :)
Now, you can get the transparency-effect in two ways, either have a white background and call all the views setAlpha() methods, or you can create a half-transparent white overlay.
If you go with the overlay, you'll have to find a way to display the opaque buttons through the overlay. This can get a bit complicated.
If you go with the first option, you can just setAlpha(1) on the opaque view to get it to show up.
The setAlpha() method is only available from api version 11+, so if you target an earlier version, you might have to do it in a slightly more complicated way.
Example of setting alpha on views pre-honeycomb:
Layout for your buttons (make them however you want, just make them similar so you can loop through them):
<LinearLayout
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:tag="image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/tile"/>
<TextView
android:tag="text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF000000"
android:text="button1"/>
</LinearLayout>
In your program, when you are want to make the buttons transparent:
LinearLayout l = (LinearLayout) findViewById(R.id.button1);
((ImageView)l.findViewWithTag("image")).setAlpha(0x7F);
((TextView)l.findViewWithTag("text")).setTextColor(0x7F000000);
When you have decided on how you want to create the transparency effect, you will have to decide on how to display the overlay-text/bubble. You'll most likely want to put this in a separate layer on top of your entire layout, to make sure that it is not affected by your new view.
One way to achieve this is by changing your root layout element to a FrameLayout, and then creating/displaying in this. e.g:
<FrameLayout background="#FFFF"> <!-- white background, just in case -->
<LinearLayout>
<!-- the rest of your layout -->
</LinearLayout>
<LinearLayout visibility="gone"> <!-- this will be your overlay view -->
<ImageView /> <!-- the arrow/ring -->
<TextView /> <!-- the description -->
</LinearLayout>
</FrameLayout>
When the introduction is displayed, you set the position of the hidden overlay-view to the position of the table item to be explained, change the text to an appropriate string/resource and display the view.
When the introduction is over, you reset the alpha values of all buttons, and set the visibility of the overlay to gone again.
Since I don't have much experience with ViewStub, I would do it with LayoutInflater.
First of all, you need to have a second layout loaded on top of your current layout. The easiest is to have a FrameLayout, which has as one child your current view, and the dynamically you load the second child on the first start. When you load a content view in an Activity, it will be attached to some already created views (some DecorView, a FrameLayout, etc). So you can either re-use the existing FrameLayout, or you can create a new one.
I would vote for the second solution, since it's more stable (I just mentioned the other possibility in case you want to minimize the number of layers).
So, as a first step, wrap your current layout inside a FrameLayout, and give it an id, let's say "#id/root".
Then, in the onCreate method, you can have something like this:
setContentView(R.layout.main);
if (isFirstRun()) {
ViewGroup parent = (ViewGroup)findViewById(R.id.root); // locate the FrameLayout
LayoutInflater li = LayoutInflater.from(this); // get an instance of LayoutInflater
li.inflate(R.layout.overlay, parent);
}
So far you will have the overlay loaded. Now it's up to you to define the overlay.
To make the whitening effect, just set the following attribute on the root view in your overlay.xml layout:
android:background="#40ffffff"
To position the circle, first you need to find it's location. You can use the View.getLocationOnScreen to get the absolute coordinate of the icon (below the circle) on the screen. Then you can have two options:
either create a custom view (for the overlay) and manually draw the circle at the given location
or add the circle using an ImageView and adjust the left and top margins based on the coordinates

redundant onDraw() calls in FrameLayout

I have the following layout structure in my xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainlayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<MyEntireView
android:id="#+id/map"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</MyEntireView>
<FrameLayout
android:id="#+id/focusedlayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom">
<MyDetailedView
android:id="#+id/focus"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</MyDetailedView>
</FrameLayout>
</FrameLayout>
MyEntireView is a map (a Drawable) and MyDetailedView pops up on top of it upon onTouch() and contains only a certain piece of the whole map (also Drawable) that displays in greater detail the area which the user touched on the entire map in MyEntireView.
When MyDetailedView is shown - i.e. its setVisibility(View.VISIBLE) - there appear some transparent shapes (also some Bitmaps) on top and are redrawn regularly (e.g., 1 sec interval). The shapes are added dynamically as children of the #+id/focusedlayout as there is always a different number of them and their position is relevant to the detailed map picture.
And the problem is that each single update is processed multiple times. Each time a shape updates, then MyEntireView's onDraw() is called, MyDetailedView's onDraw() is called, and then these two are called again. And only after this the shape itself is invalidated.
As a result, this behaviour makes the shape flicker, since MyDetailedView redraws and hides the shape for 10ms or so.
I must probably be overlooking some simple logic, but I really can't get if there is a way to force only one View of a ViewGroup invalidate().
Or maybe there is an alternate structure/layout that I could use to fit my purpose?
Thanks a lot!
P.S.
The entire code is quite big to put here but I'm willing to insert any pieces upon request

Categories

Resources