I'm trying to extend a RelativeLayout object and include an embedded SurfaceView object that uses a png file in my /res/drawable folder as its background but I keep getting an error in the XML Layout editor. See the following code:
public class StopMotionRelativeLayout extends RelativeLayout
{
private Context myContext;
private SurfaceView surfaceView1;
private BitmapFactory.Options myBitmapOptions;
private final android.view.ViewGroup.LayoutParams params =
new LayoutParams(LayoutParams.FILL_PARENT, android.view.ViewGroup.LayoutParams.FILL_PARENT);
public StopMotionRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
myContext = context;
this.setBackgroundColor(Color.GREEN);
//init images
surfaceView1 = new SurfaceView(myContext,attrs);
surfaceView1.setVisibility(View.INVISIBLE);
this.addView(surfaceView1, params);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
myBitmapOptions = new Options();
myBitmapOptions.outWidth = widthMeasureSpec;
myBitmapOptions.outHeight = heightMeasureSpec;
surfaceView1.setBackgroundDrawable(new BitmapDrawable(BitmapFactory.decodeResource(this.myContext.getResources(),
R.drawable.golf1, myBitmapOptions)));
}
}
I get the following error:
NotFoundException: Could not find
drawable resource matching value
0x7F020002 (resolved name: golf1) in
current configuration.
I have seen this type of error may times now and it always happens when I try to load something from a resource file via code and not XML. Curiously, this error does not stop me from compiling the app, and the app runs without error in the emulator. I'd like to get back the use of my layout editor though...
Please help.
UPDATE: Here is the layout XML
<?xml version="1.0" encoding="utf-8"?>
<com.games.test.StopMotionRelativeLayout android:id="#+id/RelativeLayout01"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
</com.games.test.StopMotionRelativeLayout>
I wonder if it would work if you specified a value for android:background in the XML and then override it in your code.
As a workaround, you could also try calling the isInEditMode() function and not set the background in edit mode.
Related
RelativeLayout layout = (RelativeLayout) findViewById(R.id.relativer);
layout.setBackgroundResource(R.drawable.bgorange);
I want the bgorange to get retrieved from a spinner. So bgorange is a static value for now.
In general, for getting a resource programmatically you can use:
public static final Drawable getDrawable(Context context, int id) {
return ContextCompat.getDrawable(context, id);
}
So, in your exmaple you can use it like this:
RelativeLayout layout = (RelativeLayout) findViewById(R.id.relativer);
Drawable drawable = getDrawable(context, YourSpinner.getYourResBackground());
layout.setBackgroundDrawable(drawable)
Note that you need the latest support lib in your build.grade
compile 'com.android.support:support-v4:xxxxxx
It is possible to create a click effect/animation for all clicks on my application?
Since I want this behaviour for all clicks events on my application, how should I do this? Is this bad for performance or resources of the smartphone?
It is hard to know without understanding the stack that has been built, that being said I think there are some safer and less methodologies to an onclick event that is the same across the board. For one I would not change fundamental nature of the "onClick" function, the lower level you mess with the more dangerous it is. That being said I think I would create my own version/function of onclick, maybe boomClick, where boomClick creates the animation that you want. Referencing a single function will barely decrease performance at all.
So, after a day working at this I managed to accomplish the expected behaviour.
Basically, I create my own Activity class which will do the animation work with the help of a custom lib. I'll try to explain what I did for future reference:
1. Add this lib to your project:
compile 'pl.droidsonroids.gif:android-gif-drawable:1.2.3'
2. Add these dimens to your "dimens.xml" file:
<dimen name="click_animation">100dp</dimen>
<dimen name="click_compensation">50dp</dimen>
3. Make the top parent of your activities layout a "RelativeLayout" and set a custom id:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/main_layout">
... the rest of the layout ...
</RelativeLayout>
4. Create your own "Activity" class:
public class MyActivity extends AppCompatActivity {
private RelativeLayout.LayoutParams params;
public void setClickAnimation(final Activity activity) {
// if you want to change the size of the animation, change the size on the dimens.xml
int size = (int) activity.getResources().getDimension(R.dimen.click_animation);
params = new RelativeLayout.LayoutParams(size, size);
// you want the parent layout of the activity
final RelativeLayout view = (RelativeLayout) activity.findViewById(R.id.main_layout);
view.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// you maybe won't need this compensation value
int compensation = (int) activity.getResources().getDimension(R.dimen.click_compensation);
try { startAnimation(view, (int) event.getX() - compensation, (int) event.getY() - compensation); }
catch (IOException e) { e.printStackTrace(); }
}
return true;
}
});
}
private void startAnimation(RelativeLayout view, int x, int y) throws IOException {
params.leftMargin = x;
params.topMargin = y;
// those are from the lib you imported
final GifImageView anim = new GifImageView(this);
// if you don't have it yet, put the gif you want on the assets folder
final GifDrawable gifFromResource = new GifDrawable(getAssets(), "click_animation.gif");
gifFromResource.addAnimationListener(new AnimationListener() {
#Override
public void onAnimationCompleted(int loopNumber) {
anim.setVisibility(View.GONE);
gifFromResource.stop();
gifFromResource.recycle();
}
});
anim.setBackground(gifFromResource);
gifFromResource.start();
view.addView(anim, params);
}
}
5. Make your Activities extend your "Activity" class:
public class FirstScreen extends MyActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.cards_screen);
// call the method you created and pass the activity context
setClickAnimation(this);
}
}
As for resources spent: this looks like a good solution and I am getting a good performance. The application seems to not be wasting a lot of resources with this solution.
I have a custom extended TextView I am using for custom fonts within my application, but for some reason I keep getting a run-time exception where the program can't find the font in question.
The format of the directory I'm using is main > assets > fonts > Portrait-Light.ttf
I've looked everywhere for a solution but they all seem to be rounding to the same answers on SO.
CustomFontTextView.java :
public class CustomFontTextView extends TextView {
public CustomFontTextView(Context context) {
super(context);
applyCustomFont(context);
}
public CustomFontTextView(Context context, AttributeSet attrs) {
super(context, attrs);
applyCustomFont(context);
}
public CustomFontTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
applyCustomFont(context);
}
private void applyCustomFont(Context context) {
Log.e("it gets here", "custom font");
Typeface customFont = FontCache.getTypeface("Roboto-Italic.ttf", context);
setTypeface(customFont);
}
}
FontCache.java
class FontCache {
private static HashMap<String, Typeface> fontCache = new HashMap<>();
static Typeface getTypeface(String fontname, Context context) {
Typeface typeface = fontCache.get(fontname);
if (typeface == null) {
try {
typeface = Typeface.createFromAsset(context.getAssets(), "fonts/" + fontname);
} catch (Exception e) {
Log.e("failed", e.getMessage());
}
fontCache.put(fontname, typeface);
}
return typeface;
}
}
XML:
<icn.premierandroid.misc.CustomFontTextView
android:id="#+id/switch_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textSize="14sp"
android:textColor="#color/colorPrimary"
android:gravity="center_horizontal"
android:text="#string/are_you_over_18_years_old"/>
I have tried this with different formats such as .otf and with different fonts such as Roboto-Italic.ttf and another random one from dafont.com called Sunset-Clouds.ttf but still i get the error message, what is going on? this should be working. I've even updated all plugins such as Gradle, Grade-Wrapper distribution and Android gradle plugin just in case.
I have also tried the single way of doing it:
AssetManager am = context. getApplicationContext(). getAssets();
Typeface font = Typeface.createFromAsset(
am, String.format(Locale.US, "fonts/%s", "portrait-light.ttf"));
Am i missing something?
Update:
Removal of the catch reveals this stacktrace.
Process: icn.premierandroid, PID: 3829
java.lang.RuntimeException: Unable to start activity ComponentInfo{icn.premierandroid/icn.premierandroid.RegisterActivity}:
android.view.InflateException: Binary XML file line #100: Error
inflating class icn.premierandroid.misc.CustomFontTextView
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2702)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2767)
.....
Caused by: android.view.InflateException: Binary XML file line #100:
Error inflating class icn.premierandroid.misc.CustomFontTextView
at android.view.LayoutInflater.createView(LayoutInflater.java:640)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:750)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:813)
.....
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:288)
at android.view.LayoutInflater.createView(LayoutInflater.java:614)
....
Caused by: java.lang.RuntimeException: Font asset not found
fonts/GleegooRegular.ttf
at android.graphics.Typeface.createFromAsset(Typeface.java:272)
at icn.premierandroid.misc.FontCache.getTypeface(FontCache.java:21)
at icn.premierandroid.misc.CustomFontTextView.applyCustomFont(CustomFontTextView.java:28)
at icn.premierandroid.misc.CustomFontTextView.(CustomFontTextView.java:18)
at java.lang.reflect.Constructor.newInstance(Native Method)
.....
UPDATE 2:
Okay, so there is a weird work-around for this. Apparently android studio doesn't like it when you add the assets folder straight into the main folder inside app/src/main. You need to add it into the app/src/main/res folder first and then move it to the main folder. Don't have a clue why it's like this but it solved my problem.
I think the real problem is that you haven't configured your assets folder. If you had, it would look like this in Project view:
Or like this in Android view:
So you should simply add this folder as assets folder in Studio:
Click on your project structure window (or press Alt+1), then press Alt + Insert and select Folder/Assets Folder. Then put your fonts there.
Update from OP:
Okay, so there is a weird work-around for this. Apparently android studio doesn't like it when you add the assets folder straight into the main folder inside app/src/main. You need to add it into the app/src/main/res folder first and then move it to the main folder. Don't have a clue why it's like this but it solved my problem.
We wrote this class for our needs. It allows to set our custom fonts using attribute, like this:
app:customFont="Roboto-Medium.ttf"
Hope you find it useful:
public class TextViewFonted extends TextView {
private static final String TAG = "TextViewFonted";
public TextViewFonted(Context context) {
super(context);
}
public TextViewFonted(Context context, AttributeSet attrs) {
super(context, attrs);
setCustomFont(context, attrs);
}
public TextViewFonted(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setCustomFont(context, attrs);
}
private void setCustomFont(Context ctx, AttributeSet attrs) {
TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.TextViewFonted);
String customFont = a.getString(R.styleable.TextViewFonted_customFont);
if (customFont == null) customFont = "Roboto-Regular.ttf";
setCustomFont(ctx, customFont);
a.recycle();
}
public boolean setCustomFont(Context ctx, String asset) {
Typeface tf = null;
try {
tf = Typeface.createFromAsset(ctx.getAssets(), asset);
} catch (Exception e) {
Log.e(TAG, "Could not get typeface", e);
return false;
}
setTypeface(tf);
setLineSpacing(getTextSize() * 0.3f, 0.75f);
return true;
}
}
And to be able to use this custom attribute, you should add to your attrs.xml next block (so R.styleable.TextViewFonted_customFont will work):
<declare-styleable name="TextViewFonted">
<attr name="customFont" format="string"/>
</declare-styleable>
In our case, we put fonts in the root of assets folder, but you could change this behavior in method setCustomFont()
Update
To add this font, for example, we use
app:customFont="Comfortaa-Bold.ttf"
Try this....
put .ttf fonts in app > assets > fonts > Roboto-Italic.ttf
And then in onCreate method,
Typeface Roboto = Typeface.createFromAsset(getAssets(),
"fonts/Roboto-Italic.ttf");
and set font in textview,etc
textview.setTypeface(Roboto);
I have been trying to make my first android app. I am stuck with this strange problem. There are lot of people who had faced same problem (like here, here, here and lots of other places) in past but none of the solutions seems to work for me.
My problem is if I set layout file like setContentView(R.layout.MainActivity) application crashes on this function but if I directly set GLSurfaceView as content view application works just fine. I want to have a ListView and GLSurfaceView on same screen thats why I am trying to add it in XML.
Here is my layout file
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.camerafilters.CameraGLSurfaceView
android:id="#+id/CameraGLSurfaceView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<!-- <ListView
android:id="#+id/ShaderList"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
-->
</LinearLayout>
Here is relevant portion from main activity. Note that CameraGLSurfaceView is an inner class.
public class CameraMainActivity extends Activity implements
SurfaceTexture.OnFrameAvailableListener
{
private CameraGLSurfaceView _cameraGLView;
/**
* Captures frames from an image stream as an OpenGL ES texture. The image stream may come from either camera preview or video decode.
* A SurfaceTexture may be used in place of a SurfaceHolder when specifying the output destination of a Camera or MediaPlayer object
*/
private SurfaceTexture _surface;
CameraGLRenderer _renderer;
private Camera _camera;
private ListView _shaderListView;
class CameraGLSurfaceView extends GLSurfaceView
{
CameraGLRenderer renderer;
public CameraGLSurfaceView(Context context, AttributeSet attrs)
{
super(context, attrs);
// Create an OpenGL ES 2.0 context
setEGLContextClientVersion(2);
// Set the Renderer for drawing on the GLSurfaceView
renderer = new CameraGLRenderer((CameraMainActivity)context);
setRenderer(renderer);
// Render the view only when there is a change in the drawing data
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
public CameraGLRenderer getRenderer()
{
return renderer;
}
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
_cameraGLView = new CameraGLSurfaceView(this, null);
// Create a GLSurfaceView instance and set it
// as the ContentView for this Activity.
setContentView(R.layout.activity_main);
_cameraGLView = (CameraGLSurfaceView) findViewById(R.id.CameraGLSurfaceView);
_renderer = _cameraGLView.getRenderer();
}
Any pointers where i am wrong ?
First, have a look at the exception stacktrace in logcat to find the exact problem.
Then, guessing the problem is that the view specified in your XML cannot be instantiated: your inner class needs to be public static and you need to refer to it correctly in XML, like
com.example.camerafilters.CameraMainActivity$CameraGLSurfaceView
though it's cleaner to have it as a separate class instead of an inner class.
Why u calling it before onCreate ? You don't need to call it since you defined it in your xml layout.
You dont need to call it all. Just delete the line before setContentView.
You should call GLSurfaceView.setRenderer() in your Activity.onCreate()
I'm unable to call methods of a custom view ("canvasview") from the Activity that sets the layout including the view. I can't even call canvasview's "getters" from the activity.
Also, I'm passing the view to a custom class (that does not extend Activity), and I can't call canvasview's methods also from my custom class.
I'm not sure what I'm doing wrong...
GameActivity.java:
public class GameActivity extends Activity implements OnClickListener
{
private View canvasview;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.game_layout);
canvasview = (View) findViewById(R.id.canvasview);
// Eclipse displays ERROR con those 2 method calls:
int w = canvasview.get_canvaswidth();
int h = canvasview.get_canvasheight();
(...)
game_layout.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/LinearLayout2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".GameActivity" >
(...)
<com.example.test.CanvasView
android:id="#+id/canvasview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
CanvasView.java:
public class CanvasView extends View
{
private Context context;
private View view;
private int canvaswidth;
private int canvasheight;
public CanvasView(Context context, AttributeSet attrs)
{
super(context, attrs);
this.context = context;
this.view = this;
}
#Override
protected void onSizeChanged(int width, int height,
int old_width, int old_height)
{
this.canvaswidth = width;
this.canvasheight = height;
super.onSizeChanged(width, height, old_width, old_height);
}
public int get_canvaswidth()
{
return this.canvaswidth;
}
public int get_canvasheight()
{
return this.canvasheight;
}
I'm quite confused with this :?
I also have another class (it does not extend "Activity") that receives in the constructor a reference to canvasview and is also unable to "resolve" it :?
Thanks, sorry if the question is too obvious, I'm starting with Java and those kind of things are quite confusing to me ...
EDIT:
While at bed (03:00AM), thinking about it, I've noticed that Eclipse marks the line as an error because the View object does not really have the method get_canvaswidth(). Only the child "CanvasView" method has it. Thus, my problem can be solved with upcast:
int w = ((CanvasView) canvasview).get_canvaswidth();
I mean that I receive a view as parameter, but as I now it's really a view child, I should be able to use upcast to call "child's" methods. Now eclipse does not generate errors but w and h always report 0 :-? . I've also tested of not using upcast as has been suggested in an answer, and sending and receiving CanvasView objects in the calls and I also get 0 for both :?
private View canvasview;
No matter what is stored in canvasview you can only call methods defined by the variable type. You need to change this line.
private CanvasView canvasview;