So I am working on an Android application using Android 4.0 Library.
One of the activities of this application is made up of a RelativeLayout that has an image background and a toggle button.
The background image of the layout must change when the user toggles the button.
So it must be changed from inside the activity.java class:
if (toggleButton.isChecked()){
// Change the background of the activity to image 2 (for example)
}
else{ // when toggle button is off
// Change it back to image 1
}
Please help me with this. Thank you :)
You use the method, setBackground in the View class:
if (toggleButton.isChecked()){
// Change the background of the activity to image 2 (for example)
View myView = this.findViewById(yourViewId);
myView.setBackgroundResource(yourImage);
}
else{ // when toggle button is off
// Change it back to image 1
// Change the background of the activity to image 2 (for example)
View myView = this.findViewById(yourViewId);
myView.setBackgroundResource(yourOtherImage);
}
Related
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.
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.
This is a scaled down version of the actual problem. To recreate the difficulty I am facing.
I have taken the example from the official website developer.android.com to cite my problem.
Building a Flexible UI
MainActivity has 2 layouts. One is for the default(portrait in small screen devices) layout in the layout folder. The other layout for both large-screen and landscape mode, kept in layout-large and layout-land folder.
Default layout for activity_main.xml contains only one FrameLayout (R.id.fragment_container) in which I add and replace 2 fragments that I create, dynamically.
The other layout is same for both the layout-land and layout-large folders. It has 2 static fragments [R.id.headlines_fragment - to display a list of headlines] and [R.id.article_fragment - to display the details when headlines are selected]. Horizontally placed. One on the left to show the lists and the one on the right to show details.
This is the code for MainActivity.java which controls all the fragments :
public class MainActivity extends Activity implements OnHeadLineSelectedListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_article);
if(findViewById(R.id.fragment_container) != null) {
if(savedInstanceState != null) {
return;
}
HeadlinesFragment firstFragment = new HeadlinesFragment();
firstFragment.setArguments(getIntent().getExtras());
getFragmentManager().beginTransaction().add(R.id.fragment_container, firstFragment).commit();
}
}
#Override
public void onArticleSelected(int position) {
ArticleFragment articleFrag = (ArticleFragment) getFragmentManager().findFragmentById(R.id.article_fragment);
if(articleFrag != null && articleFrag.isVisible()) {
articleFrag.updateArticleView(position);
} else {
articleFrag = new ArticleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction().replace(R.id.fragment_container, articleFrag);
transaction.addToBackStack(null);
transaction.commit();
}
}
}
As soon as the activity is started, I check if fragment_container that is the FrameLayout is present or not. If it is not present, then the layout with the 2 fragments has been loaded. Hence no need to add the fragments dynamicaly as they are already present.
Otherwise, I check if the savedInstanceState is null or not. If null, then I create a new HeadlinesFragment and add it to the frame. If it is not null, then it means that the activity has already been created previously, and hence the HeadlinesFragment must have been added already. No need to add it again. So, return.
And onArticleSelected() method replaces the existing fragment in the frame with the ArticleFragment, or if in the other layout, it simply updates the fragment as it is already present. It is called from the HeadlinesFragment when an item is selected.
Now, this all works perfectly well if we enter the activity in portrait mode and then change the orientation. No problem. Flawless.
But if we enter the activity in the landscape mode, as soon as I change the orientation to the portrait mode, a blank screen is shown.
The reason being, the onCreate() is called, and the savedInstanceState returns as not null. Hence, the HeadlinesFragment is not created and added to the frame.
And yes, if I remove that check, then the app works fine, but that will mean that a new HeadlinesFragment is created and added to the frame each time and gets stacked on top of eachother. Which is not at all desirable.
I cannot implement this by just finding out the orientation and applying the appropriate layout. Because, in large-screen devices, even if it is in portrait mode, it is supposed to show both the fragments at once.
I have tried many convoluted logic. But nothing seems to work. Any help is appreciated.
Entering activity in portrait mode
1> List Items are shown.
2> Clicking items replaces the fragment with the ArticleFragment (details).
3> Changing the orientation, shows both side by side. Everything works.
--->
Entering activity in landscape mode
1> Both the list and details are shown. Everything works.
2> But as soon as the orientation changes, you get the blank screen. As Headlines fragment is not created and added.
-->
It would be really helpful if someone could guide me as to how I can solve this problem. And as the actual project is huge, and this logic has already been implemented, a drastic change in logic is not an option anymore as that will mean re writting thousands of lines of code. Thank you. :)
Ok. I got the problem. The problem is we are using Fragments in xml layouts in large devices.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.android.fragments.HeadlinesFragment"
android:id="#+id/headlines_fragment"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.android.fragments.ArticleFragment"
android:id="#+id/article_fragment"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
So android is trying to catch the Fragments in savedInstanceState in MainActivity. When screen rotates, system tries to restore the above Fragments even though different layout loads in potrait mode. And so system considers that the article_fragment is also available on the right side and it tries to update it on click on the Headline.
So, What's the solution ?
I have changed a little code in MainActivity and nothing else :-)
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
setContentView(R.layout.news_articles);
// Check whether the activity is using the layout version with
// the fragment_container FrameLayout. If so, we must add the first fragment
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
/*if (savedInstanceState != null) {
return;
}*/
// Create an instance of ExampleFragment
HeadlinesFragment firstFragment = new HeadlinesFragment();
// In case this activity was started with special instructions from an Intent,
// pass the Intent's extras to the fragment as arguments
firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
}
So what I have done just I told the system that I don't want any thing restored by using super.onCreate(null); so its restoring nothing now.
About the blank screen you are getting
Whenever you start activity in landscape mode. It loads large display by default. Without entering into if satement. Because it can't get fragment_container in landscape mode. And then you rotate screen to load portrait layout and system gets savedInstanceState != null and it returns without loading HeadlinesFragment. So you get bank screen.
So I have commented If statement as you can notice.
/*if (savedInstanceState != null) {
return;
}*/
So now It load everything correctly.
No issue
Download the code sample from this Developer site and modify it according to your needs.
Build Dynamic UI with fragments
Its super and reliable code.
I'm sure there are reasons why the Fragment was rendered correctly in Portrait -> Landscape and not in the reverse but one thing is very clear about the Activity lifecycle
Called when the activity is starting. This is where most
initialization should go: calling setContentView(int) to inflate the
activity's UI, using findViewById(int) to programmatically interact
with widgets in the UI, calling managedQuery(android.net.Uri,
String[], String, String[], String) to retrieve cursors for data
being displayed, etc.
Note the part that says This is where most initialization should go. By exiting after checking that savedInstanceState is null, you're leaving it up to the super class to restore your Fragment which is not a good idea considering the previous view was destroyed.
My advice, inspect the content of savedInstanceState instead of just checking if it is null. Ensure that it contains enough data to restore your previous state, if not initialize your Fragment.
The best practices with Fragments involve implementing all necessary methods to monitor the state.
I have this code which allows me to set the current device wallpaper as my activitys background.
public void wallpaperBackground()
{
WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
Drawable wallpaperDrawable = wallpaperManager.getDrawable();
View root = findViewById(R.id.home_view);
root.setBackground(wallpaperDrawable);
}
The problem is, if I change the wallpaper via settings it doesn't update on my app and I'm left with the previous wallpaper. How can I fix this?
It is quite easy actually:
Just call your wallpaperBackground() method in onResume() in your activity.
This will automatically apply the correct wallpaper when the activity resumes after the user comes back from another app.
I want to make an activity that can be opened above ANY app.
Normally, even when the activity is set as dialog, when you switch to my app, you see my app, and in the background you see the launcher:
BUT, I want the app will go above any app like this: (made in photoshop):
I did see this question Creating a system overlay window (always on top), but in ICS there is no functionallity to the layout.
Furthermore, I want to give a dialog box from my app without minimizing the other app...
there are plenty of apps that show a floating view on top of everything like : airbrowser , LilyPad , Stick it , AirTerm , Smart Taskbar , aircalc ...
anyway , in order to achieve this feature , you must have a special permission called "android.permission.SYSTEM_ALERT_WINDOW" , and use something like that:
final WindowManager.LayoutParams param=new WindowManager.LayoutParams();
param.flags=WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
final View view=findViewById(R.id.my_floating_view);
final ViewGroup parent=(ViewGroup)view.getParent();
if(parent!=null)
parent.removeView(view);
param.format=PixelFormat.RGBA_8888;
param.type=WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
param.gravity=Gravity.TOP|Gravity.LEFT;
param.width=parent!=null?LayoutParams.WRAP_CONTENT:view.getLayoutParams().width;
param.height=parent!=null?LayoutParams.WRAP_CONTENT:view.getLayoutParams().height;
final WindowManager wmgr=(WindowManager)getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
wmgr.addView(view,param);
// TODO handle overlapping title bar and/or action bar
// TODO you must add logic to remove the view
// TODO you must use a special permission to use this method :android.permission.SYSTEM_ALERT_WINDOW
// TODO if you wish to let the view stay when leaving the app, make sure you have a foreground service running.
I'm one of the developers of the Tooleap SDK, and we also dealt with this issue.
Basically, you don't need to use the SYSTEM_ALERT_WINDOW to display an activity on top of another one. You can just display a regular "shrinked" Activity with a transparent background.
To make a "shrinked Activity, change the activity window layout params of height and width:
WindowManager.LayoutParams params = getWindow().getAttributes();
params.x = ...;
params.y = ...;
params.width = ...;
params.height = ...;
this.getWindow().setAttributes(params);
To make a transparent background add to your activity definition in the manifest file:
android:theme="#android:style/Theme.Translucent"
That way, you can create the illusion of a floating activity:
Note that only the foreground activity will be resumed, while the background one is paused. But for most apps this shouldn't be an issue.
Now all that remains is when to launch the floating activity.
Here is an example of a "floating" calculator app using a regular activity. Note that the activity below the calculator belongs to another app.