I'm trying to draw the canvas on widget, but I have nothing. I have a form with imageviev on which the bitmap, which I will draw all the beauty, but it did not draw ... Here is a piece of my code, what it not work?
My provider where i draw
public class MainActivity extends AppWidgetProvider {
private Bitmap bmp;
private RemoteViews views;
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
views = new RemoteViews(context.getApplicationContext().getPackageName(), R.layout.main);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.RED);
paint.setTextSize(16);
paint.setAntiAlias(true);
paint.setTypeface(Typeface.MONOSPACE);
Bitmap bmp = Bitmap.createBitmap(100, 16, Bitmap.Config.ALPHA_8);
Canvas c = new Canvas(bmp);
c.drawText("fdgfdgfdgfdfdfdgGFDFGFDDDDG", 0, 0, paint);
views.setImageViewBitmap(R.id.imageView1, bmp);
}
}
and my main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/widget_layout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dip"
android:background="#drawable/ic_launcher" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ImageView>
It looks like you may have several problems, but this may help with a couple.
First your onUpdate method needs to be able to service each of the widgets whose IDs are being passed to you. Note that users can create several instances of your widget on one or more screens at the same time. So the first thing you need to do is create a loop to draw into all of them.
Next, you need to notify the widget manager when each of those widgets is ready to be updated.
You may also need to use a different BitMap depth. ALPHA_8 doesn't work in my test showing only black but ARGB_4444 does.
Note also that you Bitmap and RemoteViews member variables are hiding the ones that you create in the body of your method. I recommend removing the RemoteViews member, and the stack Bitmap.
Putting this all together, here is what the result will look like:
public class MainActivity extends AppWidgetProvider {
private Bitmap bmp = Bitmap.createBitmap(100, 16, Bitmap.Config.ARGB_4444);
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for(int appWidgetId : appWidgetIds) {
RemoteViews views = new RemoteViews(...);
...
views.setImageViewBitmap(R.id.imageView1, bmp);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
You may face other problems as well. For instance, it is important that all graphics calls be performed on the graphics thread. Implementing a widget is much more complicated than an app. Good luck!
try to put different values for x and y. Currently it is drawing but out side the bounds of bitmap... so try that..
You need to call
appWidgetManager.updateAppWidget(appWidgetIds, views);
Related
I have a class GameActivityas follows (I just post the relevant part):
public class GameActivity extends AppCompatActivity {
// initiate variables
private GameView mGameView;
private Display mDisplay;
private Point mSize;
public static int window_width = 0;
public static int window_height = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
mDisplay = getWindowManager().getDefaultDisplay();
mSize = new Point();
mDisplay.getSize(mSize);
window_width = mSize.x;
window_height = mSize.y;
// initialize game view object
mGameView = new GameView(this);
// adding it to the content view
//setContentView(mGameView);
setContentView(R.layout.activity_game);
}
And I have the class GameView as follows with two constructors because of the custom view (also just the relevant parts to keep it clear):
private Context mContext;
//These objects will be used for drawing
private SurfaceHolder mSurfaceHolder;
private Paint mPaint;
public GameView(Context context){
super(context);
Log.d("TEST","Constructor 1");
init(context, null);
// clear all lists
...
// create object of map in beginning of game
...
// create objects of HQs
...
// create other sprite objects and add them to lists
...
}
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d("TEST","Constructor 2");
init(context, attrs);
}
And I call in both of the constructors the init() method
private void init(Context context, AttributeSet attrs) {
mContext = context;
mSurfaceHolder = getHolder();
mPaint = new Paint();
mPaint.setColor(Color.DKGRAY);
}
My run() is like this:
#Override
public void run(){
Log.d("TEST","RUN");
long current_time = 0;
long old_time;
while (playing) {
old_time = current_time;
// update the game
update();
//to draw the frame
//onDraw(canvas);
draw();
//to control (lets the game sleep for some milliseconds)
control();
current_time = SystemClock.elapsedRealtime();
frametime = current_time - old_time;
framerate = 1000/ (current_time - old_time);
}
}
And the draw() (where the mistake happens) is like this:
private void draw() {
// BUG: mSurfaceHolder.getSurface.isValid ist niemals valid
Canvas canvas;
//if(true){
//checking if surface is valid
if (mSurfaceHolder.getSurface().isValid()) {
Log.d("Test","DRAW");
//locking the canvas
canvas = mSurfaceHolder.lockCanvas();
//drawing a background color for canvas
canvas.drawColor(Color.GREEN);
// call draw methods here
// bigger objects should be called before smaller objects for visibility
draw_ground(canvas);
draw_hq(canvas);
draw_soldiers(canvas);
draw_bullets(canvas);
draw_HUD(canvas);
//Unlocking the canvas
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
No the question is:
If I in the first class GameActivity pass the object mGameView of the class GameView to setContentView everything is fine and the draw() methods works normal which means mSurfaceHolder.getSurface().isValid()) is valid.
But if I put in the first class setContentView(R.layout.activity_game); then mSurfaceHolder.getSurface().isValid()) is never valid. Everything else works (e.g. I hear the game sound) but he doesn't draw. If I don't check for mSurfaceHolder.getSurface().isValid()) then the game crashes because in the code below in draw() the canvas is empty.
My aim is to create a custom view and overlay this with buttons.
Here is the XML:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".GameActivity">
<!--Test-->
<com.example.mirco.battlefront.GameView
android:id="#+id/gameView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:id="#+id/button"
android:layout_width="119dp"
android:layout_height="wrap_content"
android:text="Button" />
<!--Test-->
</FrameLayout>
The XML seems to be correct because Android Studio recognizes it (custom view with overlayed button). But he doesn't draw in it.
Like I said with setContentView(mGameView); everything is fine except that I only have one view where I can't overlay buttons and so on which is not what I want.
What am I doing wrong? I tried to follow that LINK as good as possible. I am trying for days now to solve this but also can't find the solution anywhere.
Instead of
mGameView = new GameView(this);
// adding it to the content view
//setContentView(mGameView);
setContentView(R.layout.activity_game);
You should do
setContentView(R.layout.activity_game);
mGameView = (GameView)findViewById (R.id.gameView);
Then the mGameview is the one in your layout.
I am absolulty new in Android and I am doing some experiment with the Canvas object.
I am trying to add it to a Canvas and then show it into a retrieved ImageView (I know that this is not the standard and simplest way to show an image into an ImageView but this is only a simplified experiment for a further more complex thing that I have to do using Canvas).
So I have a star.png (a 32x32 px icon into my /res/drawable/ folder) and I am trying to
This is my activity_main.xml layout configuration file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<ImageView
android:id="#+id/star_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
This layout contain the ImageView having id=star_container that is where I have to draw the image using the Canvas
And this is the MainActivity class that handle this view:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView imgView = (ImageView) findViewById(R.id.star_container);
Canvas canvas;
Bitmap star = BitmapFactory.decodeResource(getResources(), R.drawable.star);
Bitmap output = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
canvas = new Canvas(output);
canvas.drawBitmap(star, star.getWidth() + 2, 0, null);
imgView.setImageDrawable(new BitmapDrawable(getResources(), output));
}
}
So into the onCreate() method I retrieve the ImageView where the image have to be placed. Then I create a Bitmap object retrieving the star.png image from the resoruces that will be added to the otuput Bitmap using the Canvas.
Finally I set this output Bitmap into the retrieve ImageView.
So I expected that, when the app is running, the previous ImageView contains the star.png image but it does not appear.
I have no error when I execute this app but the only output that I obtain is the textual message defined into the TextView.
Why? What is wrong? What am I missing? How can I modify this code and let it work?
According to the documents:
left: The position of the left side of the bitmap being drawn
The problem is here :
canvas.drawBitmap(star, star.getWidth() + 2, 0, null);
You're using a value for left that is out of bitmap bounds, So nothing is drawn, try this: canvas.drawBitmap(star, 0, 0, null);
Android API provides another method to set Bitmap as drawable directly :
ImageView#setImageBitmap(Bitmap bm)
I think it's a better practice to implement a custom View and explore Canvas capabilities there (in onDraw method)
another way - using rect area:
Canvas canvas;
Bitmap star = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
Bitmap output = Bitmap.createBitmap(star.getWidth(), star.getHeight(), Bitmap.Config.ARGB_8888);
canvas = new Canvas(output);
Rect source = new Rect(0, 0, star.getWidth(), star.getHeight());
canvas.drawBitmap(star, null, source, null);
imgView.setImageBitmap(output);
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 have used custom text for widget but i know that widget can't support custom text mean TextView with our TTF file
So i have used below code for support
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
......................
remoteViews.setTextColor(R.id.textViewGuj, settings.getInt(
Const.Mean_Pref_Color_Key,
context.getResources().getColor(R.color.orange)));
remoteViews.setTextViewText(R.id.textViewEng, engWord);
remoteViews.setImageViewBitmap(R.id.textViewGuj,
buildUpdate(meaning));
remoteViews.setOnClickPendingIntent(R.id.LinearLayout01,
pendingIntent1);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
}
public Bitmap buildUpdate(String time)
{
Bitmap myBitmap = Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_4444);
Canvas myCanvas = new Canvas(myBitmap);
Paint paint = new Paint();
Typeface clock = Typeface.createFromAsset(mContext.getAssets(),"fonts/Gujarat.ttf");
paint.setAntiAlias(true);
paint.setSubpixelText(true);
paint.setTypeface(clock);
paint.setStyle(Paint.Style.FILL);
paint.setColor(settings.getInt(
Const.Mean_Pref_Color_Key,
mContext.getResources().getColor(R.color.orange)));
//paint.setColor(Color.WHITE);
paint.setTextSize(20);
paint.setTextAlign(Align.CENTER);
myCanvas.drawText(time, 80, 60, paint);
return myBitmap;
}
and for update ImageView
remoteViews.setImageViewBitmap(R.id.textViewGuj,
buildUpdate(meaning));
Now my problem is that my widget is showing me nothing it goes dark black nothing is shown in widget no any error is generating can any one help me?
I think there is a problem in your below code
remoteViews.setTextColor(R.id.textViewGuj, settings.getInt(
Const.Mean_Pref_Color_Key,
context.getResources().getColor(R.color.orange)));
just remove this line because you are using ImageView and there is no any TextColor Property for ImageView
Hope works fine :)
I'm attempting to make a simple widget that displays the battery percentage both textually and graphically in a widget. The textual part works without problem, but I'm having great difficulty getting the widget to graphically update.
Graphically, I have a battery image that I clip according to battery percentage. I'm attempting to use ClipDrawable for this.
battery_widget_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/widgetLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:padding="#dimen/widget_padding"
android:background="#drawable/battery_clip_layer" >
<TextView
android:id="#+id/batteryPercentageWidgetTextView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="#string/battery_percentage_widget_default"
android:gravity="center" />
</LinearLayout>
battery_clip_layer.xml (ie a ClipDrawable)
<clip xmlns:android="http://schemas.android.com/apk/res/android"
android:clipOrientation="vertical"
android:gravity="bottom"
android:drawable="#drawable/battery_shape" />
BatteryService.java - a service that receives battery events and updates the widget via a RemoteViews
public class BatteryService extends Service {
private static final String LOG = BatteryService.class.getName();
private final AtomicInteger batteryPercentage = new AtomicInteger(100);
private final BroadcastReceiver batteryUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
int level = intent.getIntExtra("level", 0);
batteryPercentage.set(level);
updateWidget();
}
};
#Override
public void onCreate() {
super.onCreate();
registerReceiver(batteryUpdateReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Log.i(LOG, "Created...");
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(batteryUpdateReceiver);
Log.i(LOG, "Destroyed...");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(LOG, "Started...");
updateWidget();
return super.onStartCommand(intent, flags, startId);
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
private void updateWidget() {
final int percentage = batteryPercentage.get();
log.i(LOG, "Updated... " + percentage);
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.battery_widget_layout);
remoteViews.setTextViewText(R.id.batteryPercentageWidgetTextView, percentage + "%");
//ATTEMPT 1 - no cigar
remoteViews.setInt(R.drawable.battery_clip_layer, "setLevel", 5000);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
appWidgetManager.updateAppWidget(new ComponentName(this, BatteryAppWidgetProvider.class), remoteViews);
}
private void updateWidgetAttempt2() {
final int percentage = batteryPercentage.get();
Log.i(LOG, "Updated... " + percentage);
//ATTEMPT 2 - still no cigar
Drawable clipLayer = getApplicationContext().getResources().getDrawable(R.drawable.battery_clip_layer);
if (clipLayer instanceof ClipDrawable) {
ClipDrawable clipDrawable = (ClipDrawable) clipLayer;
int level = clipDrawable.getLevel();
if (clipDrawable.setLevel(10000)) {
clipDrawable.invalidateSelf();
}
Log.i(LOG, "Updated clip amount..." + level + " -> " + ((ClipDrawable) clipLayer).getLevel());
}
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.battery_widget_layout);
remoteViews.setTextViewText(R.id.batteryPercentageWidgetTextView, percentage + "%");
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
appWidgetManager.updateAppWidget(new ComponentName(this, BatteryAppWidgetProvider.class), remoteViews);
}
}
In BatteryService please have a look at the two different attempts to update the widget (updateWidget() and updateWidgetAttempt2()). Neither attempt is successful.
I feel that I'm doing something fundamentally wrong. I'm most grateful for any help/advice! :)
You are correct in that you need to set the level on a Drawable instance, but unfortunately you can't get at the Drawable because it's in a remote view.
However, ImageView does have a method called setImageLevel, which you can call remotely. My suggestion would be to put the Drawable in an ImageView instead of as the background of the container, and then use setInt(R.id.image_view, "setImageLevel", level).
Here's what the layout should look like:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/widgetLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="#dimen/widget_padding" >
<ImageView
android:id="#+id/battery"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/battery_clip_layer" />
<TextView
android:id="#+id/batteryPercentageWidgetTextView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="#string/battery_percentage_widget_default"
android:gravity="center" />
</FrameLayout>
And the updateWidget() method:
private void updateWidget() {
final int percentage = batteryPercentage.get();
log.i(LOG, "Updated... " + percentage);
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.battery_widget_layout);
remoteViews.setTextViewText(R.id.batteryPercentageWidgetTextView, percentage + "%");
remoteViews.setInt(R.id.battery, "setImageLevel", percentage * 100);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
appWidgetManager.updateAppWidget(new ComponentName(this, BatteryAppWidgetProvider.class), remoteViews);
}
The two attempts you made regarding using a ClipDrawable in a widget are incorrect. In the first attempt you use the setInt() method incorrectly as that method expects the first int parameter to be the id of a View from the widget's layout(and not a drawable reference like you try), also there isn't a setLevel() method on any View subclass from the Android SDK, that method is present in the Drawable class. Your second approach is also incorrect as all you do is load a Drawable, set its level and that's all, that newly created Drawable has no relation to the widget's LinearLayout root.
To make the ClipDrawable work you'll need to set its level to the desired value while the drawable is already set as the LinearLayout's background or when you have the possibility of setting it as the background. Unfortunately I don't see how you could do this when the layout is used in a widget.
You'll need to use a workaround(Jschools provided one).