I have a problem with a simple piece, and I can't understand what is going wrong. What is the problem here? It should be simple.
I'm trying to do a 2-layer layer-list with one layer as a clip. Here is clip_source.xml which I made by instructions:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="#drawable/img_spare"/>
<item android:id="#+id/clip">
<clip
android:clipOrientation="horizontal"
android:drawable="#drawable/img_filled"
android:gravity="left" />
</item>
</layer-list>
The Activity is simple :
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView img = (ImageView) findViewById(R.id.imageView1);
LayerDrawable ld= (LayerDrawable) getResources().getDrawable(R.drawable.clip_source);
ClipDrawable cd=(ClipDrawable) ld.findDrawableByLayerId(R.id.clip);
cd.setLevel(4000);
//((ClipDrawable) ld.findDrawableByLayerId(R.id.clip)).setLevel(4000);
img.setImageDrawable(ld);
}
}
cd.setLevel(4000) (or if I try getLevel()) crashes everything with an unknown Exception. Is there a simple way to fix this?
Related
I am writing an app which has both light and dark modes as declared here:
styles.xml
<style name="Noon" parent="Theme.AppCompat.NoActionBar">
<item name="upper_bg">#drawable/day_sky_top</item>
<item name="lower_bg">#drawable/day_sky</item>
<item name="android:statusBarColor">#color/colorPrimaryDark</item>
</style>
<style name="Night" parent="Theme.AppCompat.NoActionBar">
<item name="upper_bg">#drawable/night_sky_top</item>
<item name="lower_bg">#drawable/night_sky</item>
<item name="android:statusBarColor">#color/colorNightDark</item>
</style>
By following this answer, I created the following file:
/res/values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="customAttrs">
<attr name="upper_bg" format="reference" />
<attr name="lower_bg" format="reference" />
</declare-styleable>
</resources>
And customized my ImageViews like this:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/top_bg"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.65"
android:src="?attr/upper_bg"/>
<ImageView
android:id="#+id/bottom_bg"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.35"
android:src="?attr/lower_bg"/>
</LinearLayout>
(Note that this is part of the code, all tags are properly closed.)
Everything works well provided I have:
boolean night = true;
setTheme(night ? R.style.Night : R.style.Noon);
setContentView(R.layout.activity_main); // or whatever activity I'm in.
on every single Activity of my app. Is there a way to run this code ONCE so that my theme changes globally?
You can still add a BaseActivity to then override override fun onCreate(), delegating it the responsibility of setTheme() for any other Activity that inherits from it.
override fun onCreate(savedInstanceState: Bundle?) {
val night = true
setTheme(if (night) R.style.Night else R.style.Noon)
}
if you don't prefer BaseActivity, you can create an extension function somewhere in charged of set theme according user preferences:
fun Activity.setTheme() {
val night = true
// Or even have more than two theme styles
this.setTheme(if (night) R.style.Night else R.style.Noon)
}
To then be called like this:
override fun onCreate(savedInstanceState: Bundle?) {
setTheme()
}
UPDATE: Java code
This class would be your base class for each required Activity
public abstract class BaseActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
boolean night = true;
setTheme(night ? R.style.Night : R.style.Noon);
}
}
So now, imagine you have to implement "Feature 1" and "Feature 2", so that, you just inherits them from BaseActivity.
"Feature 1":
public class Feature1Activity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // <- BaseActivity's onCreate() will set theme for you
setContentView(R.layout.activity_feature_1);
}
}
"Feature 2":
public class Feature2Activity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // <- BaseActivity's onCreate() will set theme for you
setContentView(R.layout.activity_feature_2);
}
}
Yes you can !
You have to use method setDefaultNightMode() of class AppCompatDelegate that take the value of the theme you want to apply , the values available is MODE_NIGHT_NO & MODE_NIGHT_YES & MODE_NIGHT_FOLLOW_SYSTEM for light , night and phone default theme respectively , you can do something like this :
AppCompatDelegate.setDefaultNightMode(THE_MODE_YOU_WANT);
Its important to note that starting with AppCompat v1.0.0 , when this method get called it recreates all running activity to apply the theme .
If you are running Api 29 or higher , you should consider using Force Dark , you can check it all in the official document to get better understanding of it .
Enjoy !
I want to transition the color of my background when a button is clicked but my app seems to crash once I press the button. The button runs the TransitionDrawable code. I have set my background to the drawable folder containing the transition.Could anyone help me? Thank you very much!
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void colourChangeButton(View view) {
final TransitionDrawable transition = (TransitionDrawable) view.getBackground();
transition.startTransition(1000);
}
}
Here are my drawable files that define the colours:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<color android:color="#2575fc"></color>
</item>
</selector>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<color android:color="#ff0844"></color>
</item>
</selector>
Here is my transition drawable file :
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/blue_background"></item>
<item android:drawable="#drawable/pink_background"></item>
</transition>
You are almost there!
You need to set the transition drawable as the background of your view.
Let's say your activity's layout xml is called activity_main.xml and your
transition drawable file is called transition.xml.
In the root layout of your activity_main.xml:
<LinearLayout
...
id="#+id/main_layout"
background="#drawable/transition">
<Button
id="#+id/start_transition_button"
... />
...
In MainActivity.java:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewGroup layout = (ViewGroup) findViewById(R.id.main_layout);
TransitionDrawable transition = (TransitionDrawable) layout.getBackground();
Button button = (Button) findViewById(R.id.start_transition_button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
transition.startTransition(500);
}
}
}
}
Here is a good post about it:
https://proandroiddev.com/transitiondrawable-small-gems-of-the-android-framework-4dcdd3c83319
So I currently have this code in my oncreate. I really want to create an animation as soon as the user opens the app using a png sequence.
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_home);
view = (ImageView) findViewById(R.id.imageAnimation);
view.setBackgroundResource(R.drawable.animation_list_filling);
frameAnimation = (AnimationDrawable) view.getBackground();
And..
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
frameAnimation.start();
} else {
frameAnimation.stop();
}
}
And I have created an xml file
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:duration="100"
android:drawable="#drawable/tugs_00000"/>
<item
android:duration="100"
android:drawable="#drawable/tugs_00001"/>
<item
android:duration="100"
android:drawable="#drawable/tugs_00002"/>
...
<item
android:duration="100"
android:drawable="#drawable/tugs_00043"/>
</animation-list>
But once I run the app, it keeps on crashing. There were no errors in Android Studio present.
I'm trying to draw drawable consisting of circle and rectangle. It's working fine while api is set to 23. I noticed that following attributes of 'item' have been added in newest api: height, gravity, weight. Is it possible to get the effect in older sdk?
Drawable:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:height="5dp"
android:gravity="center_vertical"
android:left="5dp">
<shape android:shape="rectangle">
<size
android:width="50dp"
android:height="5dp" />
<solid android:color="#125572" />
</shape>
</item>
<item
android:width="20dp"
android:height="20dp"
android:gravity="center_vertical">
<shape android:shape="oval">
<solid android:color="#125572" />
<size
android:width="20dp"
android:height="20dp" />
</shape>
</item>
</layer-list>
Image using api 23:
Image using api 22:
I know this question is quite old but I solved this problem and maybe it is useful for someone even now.
For the solution I created two classes.
One class called MyLayerDrawable that extends LayerDrawable. The important thing to keep in mind about this class is that you can get the top and the left insets for each layer.
public class MyLayerDrawable extends LayerDrawable{
private List<Layer> layers;
//some code here
public static class Layer{
private Drawable drawable;
private int topInset;
private int leftInset;
public Layer(Drawable drawable,int topInset,int leftInset){
this.leftInset=leftInset;
this.drawable=drawable;
this.topInset=topInset;
}
public Drawable getDrawable(){
return drawable;
}
public int getTopInset(){
return topInset;
}
public int getLeftInset(){return leftInset;}
}
The second class I created is called MyImageViewPatch that owns an ImageView.
The only public method of this class is setImageMyLayerDrawable(MyLayerDrawable layerDrawable). This method delegates setting the image drawable to the ImageView that it owns:
public void setImageMyLayerDrawable(MyLayerDrawable layerDrawable){
imageView.setImageDrawable(layerDrawable);
}
But before calling the setImageDrawable method an observer object has been added to the ImageView in the MyImageViewPatch constructor. This observer is called before drawing the Drawable in the ImageView:
imageView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
MyLayerDrawable layerDrawable=(MyLayerDrawable)imageView.getDrawable();
int numberOfLayers=layerDrawable.getNumberOfLayers();
Drawable drawable;
Rect bounds;
for(int i=0;i<numberOfLayers;i++){//for each layer
drawable=layerDrawable.getDrawable(i);
bounds=drawable.getBounds();
bounds.top=layerDrawable.getLayer(i).getTopInset();
bounds.left=layerDrawable.getLayer(i).getLeftInset();
if(drawable.getIntrinsicHeight()<bounds.height()){
bounds.bottom=drawable.getIntrinsicHeight()+layerDrawable.getLayer(i).getTopInset();
}
if(drawable.getIntrinsicWidth()<bounds.width()){
bounds.right=drawable.getIntrinsicWidth()+layerDrawable.getLayer(i).getLeftInset();
}
}
return true;
}
});
As you can see before drawing the MyLayerDrawable the bounds for each Layer are adjusted based on the left and top insets; this is the key part to get the drawables drawn in the correct size instead of stretched.
Here is the shapes xml file:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:height="5dp"
android:gravity="center_vertical"
android:left="5dp"
android:top="8dp">
<shape android:shape="rectangle">
<size
android:width="50dp"
android:height="5dp" />
<solid android:color="#125572" />
</shape>
</item>
<item
android:width="20dp"
android:height="20dp">
<shape android:shape="oval">
<solid android:color="#125572" />
<size
android:width="20dp"
android:height="20dp" />
</shape>
</item>
</layer-list>
To centre the rectangle vertically I used android:top="8dp" instead of android:gravity="center_vertical".
Here is the code for the Main Activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView iv=(ImageView)findViewById(R.id.imageView);
MyLayerDrawable myLayerDrawable=MyLayerDrawable.Builder.build(getApplicationContext(),R.drawable.layer_d_example);
MyImageViewPatch myImageViewPatch=new MyImageViewPatch(iv);
myImageViewPatch.setImageMyLayerDrawable(myLayerDrawable);
}
}
I have the complete code in github. You may check it out: https://github.com/marcosbses/my_layout_drawable.git
I've been trying to get an Action Button working on my Android app, and been failing miserably. I've read so many questions and answers and nothing seems to work. The video link always just appears in the menu which I do not want
Here is the menu Xml.
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:puertogaleradivesites="http://schemas.android.com/apk/res-auto" >
<!-- Search, should appear as action button -->
<item android:id="#+id/youtube"
android:icon="#drawable/ic_play_arrow_black_24dp"
android:title="YouTube"
puertogaleradivesites:showAsAction="ifRoom"/>
<item android:id="#+id/action_settings"
puertogaleradivesites:showAsAction="never"
android:title="Settings"/>
</menu>
And here is the main Java Fragment
public class DivesiteFragment extends Fragment {
private Divesite mDivesite;
private TextView mDepthField;
private TextView mSiteDescription;
private TextView mDistance;
private ProportionalImageView mImageView;
public static final String EXTRA_SITE_ID = "com.blueribbondivers.extraSiteID";
private Context mContext;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
UUID crimeId = (UUID)getActivity().getIntent()
.getSerializableExtra(EXTRA_SITE_ID);
mDivesite = DiveSites.get(getActivity()).getDiveSite(crimeId);
mContext = getActivity().getApplicationContext();
//getActivity().getActionBar().setTitle(Html.fromHtml("<font color=\"#1c3565\">" + mDivesite.getName() + "</font>"));
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_divesite, parent, false);
Resources resources = mContext.getResources();
//setHasOptionsMenu(true);
mDepthField = (TextView)v.findViewById(R.id.divesite_display_depth);
mDepthField.setText(mDivesite.getMaxDepth());
mSiteDescription = (TextView)v.findViewById(R.id.divesite_display_description);
mSiteDescription.setText(mDivesite.getSiteDescription());
mSiteDescription.setMovementMethod(new ScrollingMovementMethod());
mDistance = (TextView)v.findViewById(R.id.divesite_display_distance);
mDistance.setText(mDivesite.getTravelTime());
mImageView = (ProportionalImageView)v.findViewById(R.id.divesite_display_imageView);
String imageName = mDivesite.getPhoto();
final int resourceID = resources.getIdentifier(imageName,"drawable",mContext.getPackageName());
mImageView.setImageResource(resourceID);
setHasOptionsMenu(true);
return v;
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.divesitemenu, menu);
super.onCreateOptionsMenu(menu,inflater);
}
}
I have the full project on Github also https://github.com/Jonnyblue/PuertoGaleraDiveSites
Any help would be greatly appreciated.
For displaying menus like Youtbe, gmail:
You should use yourapp:showAsAction="always"
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:yourapp="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" tools:context=".HomeActivity">
<item
android:id="#+id/action_search"
android:icon="#drawable/ic_search"
yourapp:showAsAction="always"
android:title="#string/search"/>
<item
android:id="#+id/action_refresh"
android:icon="#drawable/ic_refresh"
yourapp:showAsAction="always"
android:title="#string/refresh"/>
</menu>
Hope it will help you.
Your activity extends FragmentActivity. If you'd like a backward compatible action bar that works the same on all API levels (things changed significantly in API 21), you should instead extend AppCompatActivity
If you want to show an ActioBar item a as a button but not as an overflow menu, You need to define an android:orderInCategory for it. So that it doesn't comes inside the overflow menu.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_search"
android:icon="#drawable/ic_magnify"
android:orderInCategory="100"
app:showAsAction="ifRoom|collapseActionView"
android:title="#string/noticiation_count"/>
</menu>
Also as you are using fragments its safe to use this line inside the onCreate() method,
setHasOptionsMenu(true);
Tested and working perfectly with Fragments.