A fragment that hosts a SurfaceView only draws onto Canvas once - java

Good Evening,
I minimized my large project onto a small scale one which still keeps all the aspects of issue I'm having. I'm using an activity that draws SurfaceView background underneath a single Button. On press that Button adds a new fragment (to an empty FrameLayout that's in MainActivities XML layout) which as well holds SurfaceView and three buttons in ConstraintLayout. Button works as intended and calls the Fragments, but only the static layout part is being drawn(three buttons) as for SurfaceView it only draws Initial Canvas and keeps redrawing same Frame, even tho Thread keeps on calling the Draw method with updated values(in this case Color). ThreadPool is used to create two instances that call update and draw methods for SurfaceView(MaintActivity and Fragment) class objects(this part works as intended).
I'm also getting this line in Log, right about where the issue begins.
D/MY FRAGMENT VIEW: >> INIT
D/MY FRAGMENT VIEW: SURFACE CREATED
D/MY_FRAGMENT: SURFACE CREATED
D/MY FRAGMENT VIEW: SURFACE CHANGED
D/MY_FRAGMENT: SURFACE CHANGED
D/MY FRAGMENT VIEW: DRAW PLANET
D/: HostConnection::get() New Host Connection established 0xb2d1d780, tid 9982
D/MY FRAGMENT VIEW: DRAW PLANET
D/MY FRAGMENT VIEW: DRAW PLANET
D/MY FRAGMENT VIEW: DRAW PLANET
D/MY FRAGMENT VIEW: DRAW PLANET
Code samples bellow:
package com.badcompany.testfragmentview;
import android.os.Bundle;
import android.support.constraint.ConstraintLayout;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback {
private MyActivityView gameView;
private static final String TAG = "MAIN";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
gameView = findViewById(R.id.myview);
SurfaceHolder gholder = gameView.getHolder();
gholder.addCallback(this);
gameView.setLayoutParams(new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.WRAP_CONTENT, ConstraintLayout.LayoutParams.WRAP_CONTENT));
Button btn_map = findViewById(R.id.start);
btn_map.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
displayMapFragment();
return true;
}
return false;
}
});
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
Log.d(TAG, ">> SURFACE CREATED");
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
Log.d(TAG, ">> SURFACE CHANGED");
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
Log.d(TAG, ">> SURFACE DESTROYED");
}
private void displayMapFragment() {
MyFragment myFragment = new MyFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.myMapSpace, myFragment).addToBackStack(null)
.commit();
/*fragmentTransaction.add(R.id.fragment_container, simpleFragment).addToBackStack(null).commit();
mButton.setText(R.string.close);
isFragmentDisplayed = true;*/
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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">
<com.badcompany.testfragmentview.MyActivityView
android:id="#+id/myview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:id="#+id/start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<FrameLayout
android:id="#+id/myMapSpace"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
MyActivityView.java
package com.badcompany.testfragmentview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
public class MyActivityView extends ParentGameView implements SurfaceHolder.Callback {
private static final String TAG = "MY ACTIVITY VIEW";
public MyActivityView(Context context) {
super(context);
}
public MyActivityView(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
setFocusable(true);
}
public MyActivityView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyActivityView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
Log.d(TAG, "SURFACE CREATED");
MyExecutor.getInstance().execute(new GameRunnable(surfaceHolder, this));
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
Log.d(TAG, "SURFACE CHANGED");
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
Log.d(TAG, "SURFACE DESTROYED");
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
canvas.drawColor(Color.YELLOW);
Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawCircle(x, 100, 200, paint);
}
}
MyFragment.java
package com.badcompany.testfragmentview;
import android.os.Bundle;
import android.support.constraint.ConstraintLayout;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
public class MyFragment extends Fragment implements SurfaceHolder.Callback {
private static final String TAG = "MY_FRAGMENT";
private MyFragmentView myFragmentView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) {
//AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
final View rootView = inflater.inflate(R.layout.fragment_my, viewGroup, false);
myFragmentView = rootView.findViewById(R.id.myMapView);
SurfaceHolder mSurfaceHolder = myFragmentView.getHolder();
mSurfaceHolder.addCallback(this);
myFragmentView.setLayoutParams(new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.WRAP_CONTENT, ConstraintLayout.LayoutParams.WRAP_CONTENT));
Button selectGalaxyMap = rootView.findViewById(R.id.btn_select_galaxy_map);
Button selectSolarSystemMap = rootView.findViewById(R.id.btn_select_solar_map);
Button selectPlanetMap = rootView.findViewById(R.id.btn_select_planet_map);
selectGalaxyMap.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.d(TAG, "SELECTED GALAXY");
myFragmentView.setColor(1);
Toast.makeText(getActivity(), "SELECT GALAXY", Toast.LENGTH_SHORT).show();
}
});
selectSolarSystemMap.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
myFragmentView.setColor(2);
Log.d(TAG, "SELECTED SYSTEM");
Toast.makeText(getActivity(), "SELECT SYSTEM", Toast.LENGTH_SHORT).show();
}
});
selectPlanetMap.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(getActivity(), "SELECT PLANET", Toast.LENGTH_SHORT).show();
myFragmentView.setColor(3);
Log.d(TAG, "SELECTED PLANET");
}
});
return rootView;
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
Log.d(TAG, "SURFACE CREATED");
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
Log.d(TAG, "SURFACE CHANGED");
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
Log.d(TAG, "SURFACE DESTROYED");
}
}
fragment_my.xml
<com.badcompany.testfragmentview.MyFragmentView
android:id="#+id/myMapView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:background="#color/colorPrimary"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<Button
android:id="#+id/btn_select_planet_map"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:background="#null"
android:text="SELECT PLANET"
android:textStyle="bold" />
<Button
android:id="#+id/btn_select_solar_map"
app:layout_constraintRight_toLeftOf="#id/btn_select_planet_map"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:background="#null"
android:text="SELET SYSTEM"
android:textStyle="bold" />
<Button
android:id="#+id/btn_select_galaxy_map"
app:layout_constraintRight_toLeftOf="#id/btn_select_solar_map"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:background="#null"
android:text="SELECT GALAXY"
android:textStyle="bold" />
</android.support.constraint.ConstraintLayout>
package com.badcompany.testfragmentview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
public class MyFragmentView extends ParentGameView implements SurfaceHolder.Callback {
private static final String TAG = "MY FRAGMENT VIEW";
private int Colors;
public MyFragmentView(Context context) {
super(context);
init(context);
}
public MyFragmentView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MyFragmentView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public MyFragmentView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
private void init(Context context) {
getHolder().addCallback(this);
Log.d(TAG, ">> INIT ");
setFocusable(true);
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
if(Colors == 2)
drawSolarMap(canvas);
else if(Colors == 1)
drawGalaxyMap(canvas);
else drawPlanetMap(canvas);
}
private void drawGalaxyMap(Canvas canvas){
Log.d(TAG, "DRAW GALAXY");
canvas.drawColor(Color.BLACK);
Paint paint = new Paint();
paint.setColor(Color.BLUE);
canvas.drawCircle(x, 100, 100, paint);
}
private void drawSolarMap(Canvas canvas){
Log.d(TAG, "DRAW SYSTEM");
canvas.drawColor(Color.RED);
Paint paint = new Paint();
paint.setColor(Color.BLUE);
canvas.drawCircle(x, 100, 100, paint);
}
private void drawPlanetMap(Canvas canvas){
Log.d(TAG, "DRAW PLANET");
canvas.drawColor(Color.GREEN);
Paint paint = new Paint();
paint.setColor(Color.BLUE);
canvas.drawCircle(x, 100, 100, paint);
}
#Override
public void update() {
super.update();
}
public void setColor(int color){
Colors = color;
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
Log.d(TAG, "SURFACE CREATED");
MyExecutor.getInstance().execute(new GameRunnable(surfaceHolder, this));
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
Log.d(TAG, "SURFACE CHANGED");
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
Log.d(TAG, "SURFACE DESTROYED");
}
}
Extra classes that might not have relevance
GameInterface.java
package com.badcompany.testfragmentview;
/**
* Created by Donatas on 18/12/2018.
*/
import android.graphics.Canvas;
public interface GameInterface {
public void draw(Canvas canvas);
public void update();
}
ParentGameView.java
package com.badcompany.testfragmentview;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.SurfaceView;
public class ParentGameView extends SurfaceView implements GameInterface{
private static final String TAG = "PARENT_GAME_VIEW";
protected int x = 0;
public ParentGameView(Context context) {
super(context);
}
public ParentGameView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ParentGameView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public ParentGameView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public void update() {
x++;
}
#Override
public void draw(Canvas canvas){
super.draw(canvas);
}
}
MyExecutor.java
package com.badcompany.testfragmentview;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyExecutor {
private static final ExecutorService executor = Executors.newCachedThreadPool();
public static ExecutorService getInstance(){
return executor;
}
public static void ShutDown(){
executor.shutdown();
}
}
GameRunnable.java
package com.badcompany.testfragmentview;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
public class GameRunnable implements Runnable {
private static final String TAG = "GAME_RUNNABLE";
private volatile boolean running = true;
private final int maxFPS = 30;
private double averageFPS;
private Canvas canvas;
private final SurfaceHolder surfaceHolder;
private volatile ParentGameView gameView;
public GameRunnable(SurfaceHolder surfaceHolder, ParentGameView gameView) {
this.surfaceHolder = surfaceHolder;
this.gameView = gameView;
}
#Override
public void run() {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
long startTime;
long timeMillis = 1000/maxFPS;
long waitTime;
int frameCount = 0;
long totalTime = 0;
long targetTime = 1000/maxFPS;
while(running){
startTime = System.nanoTime();
canvas = null;
try {
canvas = surfaceHolder.lockCanvas();
if(canvas != null){
synchronized (surfaceHolder){
gameView.update();
gameView.draw(canvas);
}
} else {
Log.d(TAG, "Thread finished work");
return;
}
}
catch (Exception e){
e.printStackTrace();
}
finally {
if(canvas != null){
try{
surfaceHolder.unlockCanvasAndPost(canvas);
}catch (Exception e){e.printStackTrace();}
}
}
timeMillis = (System.nanoTime() - startTime)/1000000;
waitTime = targetTime - timeMillis;
try{
if(waitTime>0)
Thread.sleep(waitTime);
} catch (Exception e){e.printStackTrace();}
totalTime += System.nanoTime() - startTime;
frameCount++;
if(frameCount == maxFPS){
averageFPS = 1000/((totalTime/frameCount)/1000000);
frameCount = 0;
totalTime = 0;
//debug System.out.println("AVERAGE FPS: " + avaragFPS);
}
}
}
}
Thanks for looking into this and if you need additional info leave a comment.

Found an awful solution myself, the issue is that MainActivity does not ask for next frame because it deems fragment view to be finished, calling invalidate() method in a Thread makes the activity to keep on asking for updated views.
If you manage to find a better solution please update this post, thanks.

Related

calling a method having parameters context and attributeset from MainActivity

I have an app that displays random characters vertically on the screen (like in the matrix movie) and I would like to add buttons to change the color of these chars when pressed, but I'm unable to call the method setColor of paintTxt variable from MainActivity.
This is my code,
EffetMatrix effetMatrix = new EffetMatrix();//Error in ()
final Paint paintTxt = effetMatrix.paintTxt;
paintTxt.setColor(Color.RED);
But the editor shows an error:
EffetMatrix(Context, AttributSet) in EffetMatrix cannot be applied to ()
the EffetMatrix class code
package com.esqmo.apps.effetmatrix;
import java.util.Random;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.content.Context;
import android.widget.Button;
/**
* Created by esQmo on 04/10/2016.
*/
public class EffetMatrix extends View {
private static final Random ALEATOIRE = new Random();
private int larg, haut;
private Canvas toile;
private Bitmap toileBmp;
private int taillePolice = 40;
private int tailleColonne;
private char[] chars = "01".toCharArray();
private int[] posTxtParColonne;
public Paint peindreTxt, peindreArrPlan, peindreArrPlanBmp, peindreInitArrPlan;
public EffetMatrix(Context context, AttributeSet attrs) {
super(context, attrs);
peindreTxt = new Paint();
peindreTxt.setStyle(Paint.Style.FILL);
peindreTxt.setColor(Color.BLUE);
peindreTxt.setTextSize(taillePolice);
peindreArrPlan = new Paint();
peindreArrPlan.setStyle(Paint.Style.FILL);
peindreArrPlan.setColor(Color.BLACK);
peindreArrPlan.setAlpha(5);
peindreArrPlanBmp = new Paint();
peindreArrPlanBmp.setColor(Color.BLACK);
peindreInitArrPlan = new Paint();
peindreInitArrPlan.setColor(Color.BLACK);
peindreInitArrPlan.setAlpha(255);
peindreInitArrPlan.setStyle(Paint.Style.FILL);
}
#Override
protected void onSizeChanged(int l, int h, int ancl, int anch) {
super.onSizeChanged(l, h, ancl, anch);
larg = l;
haut = h;
toileBmp = Bitmap.createBitmap(larg, haut, Bitmap.Config.ARGB_8888);
toile = new Canvas(toileBmp);
toile.drawRect(0, 0, larg, haut, peindreInitArrPlan);
tailleColonne = larg / taillePolice;
posTxtParColonne = new int[tailleColonne + 1];
for (int x = 0; x < tailleColonne; x++) {
posTxtParColonne[x] = ALEATOIRE.nextInt(larg / 2) + 1;
}
}
private void dessineTexte() {
for (int i = 0; i < posTxtParColonne.length; i++) {
toile.drawText("" + chars[ALEATOIRE.nextInt(chars.length)], i * taillePolice,
posTxtParColonne[i] * taillePolice, peindreTxt);
if (posTxtParColonne[i] * taillePolice > larg && Math.random() > 0.980) {
posTxtParColonne[i] = 0;
}
posTxtParColonne[i]++;
}
}
private void dessineToile() {
toile.drawRect(0, 0, larg, haut, peindreArrPlan);
dessineTexte();
}
#Override
protected void onDraw(Canvas toile) {
super.onDraw(toile);
toile.drawBitmap(toileBmp, 0, 0, peindreArrPlanBmp);
dessineToile();
invalidate();
}
public void setCustomColor(int color){
peindreTxt.setColor(color);
invalidate();
}
}
note: variable peindreText = paintText
The main activity code:
package com.esqmo.apps.effetmatrix;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Paint;
import android.media.effect.Effect;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button boutton_vert = (Button) findViewById(R.id.b_v);
final Button boutton_bleu = (Button) findViewById(R.id.b_b);
final Button boutton_rouge = (Button) findViewById(R.id.b_r);
final Button boutton_rose = (Button) findViewById(R.id.b_ro);
boutton_bleu.setOnClickListener(this);
boutton_vert.setOnClickListener(this);
boutton_rouge.setOnClickListener(this);
boutton_rose.setOnClickListener(this);
}
#Override
public void onClick(View v) {
}
public void passerVert(View v) {
}
public void passerRouge(View v) {
}
public void passerRose(View v) {
}
public void passerBleu(View v) {
}
}
main.xml
<com.esqmo.apps.effetmatrix.EffetMatrix
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/arrPlan"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:background="#040404"
android:id="#+id/linearLayout">
<Button
android:layout_width="70dp"
android:layout_height="match_parent"
android:text="#string/button_vert"
android:onClick="passerVert"
android:id="#+id/b_v"
android:textAppearance="#android:color/holo_blue_dark" />
<Button
android:layout_width="70dp"
android:layout_height="match_parent"
android:text="#string/button_bleu"
android:onClick="passerBleu"
android:id="#+id/b_b"/>
<Button
android:layout_width="70dp"
android:layout_height="match_parent"
android:text="#string/button_rouge"
android:onClick="passerRouge"
android:id="#+id/b_r" />
<Button
android:layout_width="70dp"
android:layout_height="match_parent"
android:text="#string/button_rouge"
android:onClick="passerRose"
android:id="#+id/b_ro" />
</LinearLayout>
PS: I'm a noob in programming.
Sorry for my english
Don't create a new instance of the custom view again. Instead get a reference to the view using its id.
See the following code,
EffetMatrix effetMatrix = (EffetMatrix) findViewById(R.id.arrPlan);
effetMatrix.setCustomTextColor(Color.RED);
Inside EffetMatrix class create a method named setCustomColor as follows,
class EffetMatrix {
...
public void setCustomTextColor(int color){
// Set the color to the paintTxt object
paintTxt.setColor(color);
// invalidate the view to apply the changes
invalidate();
}
...
}
This is how you implement it in your code,
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
EffetMatrix effetMatrix;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
effetMatrix = (EffetMatrix) findViewById(R.id.arrPlan);
...
}
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.b_b:
effetMatrix.setCustomTextColor(Color.BLUE);
break;
case R.id.b_r:
effetMatrix.setCustomTextColor(Color.RED);
break;
case R.id.b_ro:
effetMatrix.setCustomTextColor(Color.MAGENTA);
break;
}
}
}
You must invalidate your custom view after changing paint color.
effetMatrix.invalidate();
And if you want to display your custom view in editor you must implement :
public EffetMatrix(Context context) {
this(context, null);
}
public EffetMatrix(Context context, AttributeSet attrs) {
super(context, attrs);
}
If you want to make the buttons do something, then you have to set an onClickListener for each button in your main properly. Here's an example for button_bleu,
Get rid of boutton_bleu.setOnClickListener(this); and replace it with
button_bleu.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
effetMatrix.setCustomColor(Color.BLUE);
}
});
Now just do the same for the other 3 buttons.

Surface view Black Screen Home Button

i am developing one app where just show clouds walking through the screen using surface view everything gone good until push the Button "HOME BUTTON" so when i returned to my app just showing screen in black, the rest of the app works fine just is when you push the "Home Button"
Where is my Code for the surfaceView
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class mysurfaceview extends SurfaceView implements SurfaceHolder.Callback {
private mysurfaceviewThread bbThread = null;
private int xPosition = (getHeight()/2);
private int yPosition = (getHeight()/2);
private int posicion2 = (getWidth()/2)-40;
private int posicion3 = (getWidth()/4);
private int xDirection = 1;
public mysurfaceview(Context ctx,AttributeSet attrs){
super(ctx, attrs);
getHolder().addCallback(this);
}
public void doDraw(Canvas c){
Paint paint = new Paint();
paint.setColor(Color.WHITE);
Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.myimage);
c.drawColor(Color.WHITE);
c.drawBitmap(icon, xPosition, yPosition, new Paint());
c.drawBitmap(icon,posicion2,(getHeight()/2),new Paint());
c.drawBitmap(icon,posicion3,(getHeight()/2)+40,new Paint());
}
public void surfaceCreated(SurfaceHolder holder){
if (bbThread!=null) return;
bbThread = new mysurfaceviewThread(getHolder());
bbThread.start();
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){
}
public void surfaceDestroyed (SurfaceHolder holder){
bbThread.detener();
}
public class mysurfaceviewThread extends Thread{
private boolean corriendo,pausa;
private SurfaceHolder surfaceHolder;
public mysurfaceviewThread(SurfaceHolder surfaceHolder){
this.surfaceHolder = surfaceHolder;
}
public synchronized void pausar() {
pausa = true;
}
public synchronized void reanudar() {
pausa = false;
notify();
}
public void detener() {
corriendo = false;
if (pausa)
reanudar();
}
public void run(){
corriendo=true;
while(corriendo){
xPosition+=xDirection;
posicion2+=2;
posicion3+=1;
if(xPosition==getWidth()){
xPosition = -200; }
if(posicion2==getWidth()){
posicion2 = -200; }
if(posicion3==getWidth()){
posicion3 = -200; }
Canvas c = null;
try {
c = surfaceHolder.lockCanvas();
doDraw(c);
synchronized (surfaceHolder){
}
} finally {
if(c != null) surfaceHolder.unlockCanvasAndPost(c);}
synchronized (this) {
while (pausa) {
try {
wait();
} catch (Exception e) {
}
}
}
}
}
}
public mysurfaceviewThread getThread(){
return bbThread;
}
}
Here is the code for Activity
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.SurfaceView;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Toast;
public class animadofondo2 extends Activity{
private mysurfaceview Clouds;
private mysurfaceview.mysurfaceviewThread CloudsThread;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sufeaviewfondo);
Clouds= (mysurfaceview ) findViewById(R.id.ViewsLaynubes);
}
#Override
protected void onPause() {
super.onPause();
Clouds.getThread().pausar();
Toast.makeText(this, "Pausa", Toast.LENGTH_SHORT).show();
}
#Override
protected void onRestart(){
super.onRestart();
Clouds.getThread().reanudar();
Toast.makeText(this, "Restart", Toast.LENGTH_SHORT).show();
}
#Override protected void onStart() {
super.onStart();
Toast.makeText(this, "onStart", Toast.LENGTH_SHORT).show();
}
#Override protected void onResume() {
super.onResume();
Toast.makeText(this, "onResume", Toast.LENGTH_SHORT).show();
}
#Override protected void onStop() {
super.onStop();
Nubecitas.getThread().pausar();
Toast.makeText(this, "onStop", Toast.LENGTH_SHORT).show();
}
#Override protected void onDestroy() {
super.onDestroy();
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
}
#Override
public void onBackPressed(){
Lamar(null);
finish();
}
public void Lamar (View view){
Intent i = new Intent(this, main.class);
startActivity(i);
}
}
Here is the XML for the layout
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/linear1"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.app.example.proyectsurface.mysurfaceview
android:id="#+id/ViewsLaynubes"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

Using pinchable zoom ViewGroup RelativeLayout , reduces quality of textview android

I'm using a relative layout with pinching zoom gestures enabled (The code is provided below in ZoomableViewGroup.java). And when I pinch it the quality of the TextView within it reduces. The structure of the project is very simple it has a MainActivity with the layout of main_activity.xml which the code is provided below.
main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<com.example.test.ZoomableViewGroup
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="#+id/activity">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Hello"/>
</com.example.test.ZoomableViewGroup>
ZoomableViewGroup.java
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.RelativeLayout;
public class ZoomableViewGroup extends RelativeLayout {
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1;
public ZoomableViewGroup(Context context) {
super(context);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
public ZoomableViewGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
public ZoomableViewGroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
#Override
public boolean onTouchEvent(MotionEvent event) {
mScaleDetector.onTouchEvent(event);
return true;
}
protected void dispatchDraw(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(mScaleFactor, mScaleFactor);
super.dispatchDraw(canvas);
canvas.restore();
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Apply limits to the zoom scale factor:
invalidate();
requestLayout();
return true;
}
}
}

Android - VerticalSeekBar - onStopTrackingTouch() not working

everyone! I'm new to Android development and I'm having troubles while trying to set an specific position to the seek bar's thumb.
I want to set the seek bar's progress to it's initial position (in this case 4) when the user releases the thumb, in order to do this, I'm trying to use the method onStopTrackingTouch, but it looks like it's not being called when the user releases the seek bar thumb. There are no errors indicated by Eclipse, the program runs normally, the only problem is that when I release the thumb, it does not return to the initial position.
Detail: I'm using a Vertical Seek Bar, can this be the reason of the problem?
Here's the activity code:
package renatox.play.bluetoothcommander;
import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.SeekBar;
public class MainActivity extends Activity implements SensorEventListener {
private VerticalSeekBar acelerador;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Sets the Main Activity layout
setContentView(R.layout.activity_main);
// Gets the VerticalSeekBar
acelerador = (VerticalSeekBar) findViewById(R.id.acelerador);
acelerador
.setOnSeekBarChangeListener(new VerticalSeekBar.OnSeekBarChangeListener() {
// I've tried using SeekBar.OnSeekBarChangeListener() and VerticalSeekBar.OnSeekBarChangeListener()
#Override
public void onStopTrackingTouch(SeekBar arg0) {
acelerador.setProgress(4);
Log.i("Acelerador", "O usuário soltou o acelerador");
}
#Override
public void onStartTrackingTouch(SeekBar arg0) {
Log.i("Acelerador", "O usuário pegou o acelerador");
}
// Pega os valores do acelerador quando mudam
#Override
public void onProgressChanged(SeekBar arg0, int arg1,
boolean arg2) {
int velocidade = acelerador.getProgress();
Log.i("Acelerador", "Velocidade atual: " + velocidade);
}
});
}
Here's the xml layout file:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/fundo"
android:keepScreenOn="true"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<renatox.play.bluetoothcommander.VerticalSeekBar
android:id="#+id/acelerador"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentRight="true"
android:layout_marginBottom="30dp"
android:layout_marginRight="5dp"
android:layout_marginTop="30dp"
android:max="8"
android:maxHeight="0dp"
android:minHeight="0dp"
android:progress="4"
android:progressDrawable="#drawable/progress"
android:thumb="#drawable/thumb" />
</RelativeLayout>
And here is the VerticalSeekBar class
package renatox.play.bluetoothcommander;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.SeekBar;
public class VerticalSeekBar extends SeekBar {
protected OnSeekBarChangeListener changeListener;
public VerticalSeekBar(Context context) {
super(context);
}
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VerticalSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protected void onDraw(Canvas c) {
c.rotate(-90);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setSelected(true);
setPressed(true);
if (changeListener != null)
changeListener.onStartTrackingTouch(this);
break;
case MotionEvent.ACTION_UP:
setSelected(false);
setPressed(false);
if (changeListener != null)
changeListener.onStopTrackingTouch(this);
break;
case MotionEvent.ACTION_MOVE:
int progress = getMax()
- (int) (getMax() * event.getY() / getHeight());
setProgress(progress);
onSizeChanged(getWidth(), getHeight(), 0, 0);
if (changeListener != null)
changeListener.onProgressChanged(this, progress, true);
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
}
Can anyone give me a hint of what might be happening?
Thank you very much!!
Thought I would post this as an official "answer" since it helped me with my code. (answered by Renato Henz)
I've just added this to the "VerticalSeekBar.java": #Override public void setOnSeekBarChangeListener(OnSeekBarChangeListener mListener) { this.changeListener = mListener; } If you need any further help, please tell me. Good luck with your code!
I found that adding in this code, along with the code previously mentioned, helps with some issues:
#Override
public synchronized void setProgress(int progress) {
super.setProgress(progress);
onSizeChanged(getWidth(), getHeight(), 0, 0);
}
Hello if you change your code like this it will work.
case MotionEvent.ACTION_UP:
super.onTouchEvent(event);
// your code here.
return true;
When I add in aboce setOnSeekBarChangeListner code in the VerticalSeekBar.java, the myControl.setOnseekBarChangeListener code stops working altogether. i.e. OnProgrressChanged stops.
I am trying to enable the code in my activity to the Toast fires (just a placeholder)
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
Toast.makeText(APG.this, "Change OD Done", Toast.LENGTH_SHORT).show();
}
you have to define OnSeekBarChangeListener in VerticalSeekbar then it will work
Try it in VerticalSeekBar like
private OnSeekBarChangeListener onChangeListener;
#Override
public void setOnSeekBarChangeListener(OnSeekBarChangeListener onChangeListener){
this.onChangeListener = onChangeListener;
}
If you have missed out, just call within onTouchEvent.
super.onTouchEvent(event);

Android - Creating a circular mask on video

I'm relatively new to Android development, and I was wondering if it's possible to mask a VideoView into a shape. This is what I have so far:
Expected Result
My XML for video view and layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#0088ff"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".VideoPlayerActivity" >
<FrameLayout
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="#drawable/circular_mask"
android:foreground="#drawable/circular_mask" >
<VideoView
android:id="#+id/videoView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fitsSystemWindows="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:scrollbarAlwaysDrawVerticalTrack="false" />
<Space
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</RelativeLayout>
Mask shape xml:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:padding="20dp"
android:shape="oval" >
<solid android:color="#FFFFFFFF" />
<corners android:radius="10dp" />
</shape>
Main java:
package com.example.webmvideo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.annotation.TargetApi;
import android.app.Activity;
import android.view.Menu;
import android.widget.MediaController;
import android.widget.VideoView;
import android.util.Log;
import android.media.MediaPlayer;
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class MainActivity extends Activity {
Uri srcPath = Uri.parse("android.resource://com.example.webmvideo/" + R.raw.test);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final VideoView videoView = (VideoView)
findViewById(R.id.videoView1);
videoView.setVideoURI(srcPath);
MediaController mediaController = new MediaController(this);
mediaController.setAnchorView(videoView);
videoView.setMediaController(mediaController);
videoView.setOnPreparedListener(new
MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
String TAG = null;
Log.i(TAG , "Duration = " + videoView.getDuration());
}
});
videoView.start();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Turns out it is possible to clip a video into a circle. What you're going to want to do is create your own SurfaceView class and override dispatchDraw from here you can call canvas.clipPath and pass in a Path object that contains the circle you want the video to be masked to.
Here's the view:
public class CircleSurface extends SurfaceView {
private Path clipPath;
public CircleSurface(Context context) {
super(context);
init();
}
public CircleSurface(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CircleSurface(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
clipPath = new Path();
//TODO: define the circle you actually want
clipPath.addCircle(710, 330, 250, Path.Direction.CW);
}
#Override
protected void dispatchDraw(Canvas canvas) {
canvas.clipPath(clipPath);
super.dispatchDraw(canvas);
}
}
Here's what the activity might look like
public class MainActivity extends Activity implements SurfaceHolder.Callback {
CircleSurface surface;
MediaPlayer player;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surface = (CircleSurface) findViewById(R.id.surface);
SurfaceHolder holder = surface.getHolder();
holder.addCallback(this);
player = MediaPlayer.create(this, R.raw.yourvideo);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
player.setDisplay(holder);
player.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//TODO: handle this
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
//TODO: handle this
}
}
I've done something like this mask before. you can get what you want as following steps
1) prepare a png image with the shape you want and fill it with color 0x0000000
2) use a blank layout and make sure it covers the VideView
3) now, all touchevents are captured by this blank layout
4) judge the points' colors which are contained in TouchEvent, if the color is 0x00000000 then pass the event to VideView
here is a example to get a point's color, and it runs efficently:
// build a drawingCache and draw the mask layer to the bitmap
Bitmap drawingCache = Bitmap.createBitmap(getWidth(), getHeight(),Bitmap.Config.ARGB_4444);
Canvas drawingCacheCanvas = new Canvas(drawingCache);
drawingCacheCanvas.clipRect(x, y, x + 1, y + 1);
draw(drawingCacheCanvas);
int color = drawingCache.getPixel(x, y);
if (color == getMaskColor()) {
//TODO dispatch event to VideoView and let it to handle event
}

Categories