Android - Creating a circular mask on video - java

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
}

Related

A fragment that hosts a SurfaceView only draws onto Canvas once

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.

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.

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: my custom spinner-item works, but not correct on Lollipop - why?

I have a problem.
I made a custom spinner-item, which shows a text and a image.
this works on my devices: Android 2.3.7 and 4.0.4 - it shows the text and the image.
but doesn't work on my device: Android 5.0.2 - it shows only the text, without the image.
i don't get the reason why.. somebody an idea?
--
my Java-Code:
SimpleCursorAdapter adapter = new SimpleCursorAdapter(
getActivity(),
R.layout.spinner_item,
MainActivity.db.getList(),
new String[]{DatabaseHandler.ENTRY_LANGUAGE.NAME, DatabaseHandler.ENTRY_LANGUAGE.TTS},
new int[]{R.id.text, R.id.sound},
0);
adapter.setDropDownViewResource(R.layout.spinner_item);
adapter.setViewBinder(new MyViewBinder());
final Spinner spinner = (Spinner) view.findViewById(R.id.mlang);
spinner.setAdapter(adapter);
my spinner_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<com.example.app.common.CheckedRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#color/spinner_bg">
<CheckedTextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingLeft="13dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#drawable/spinner_selected_text"
android:textSize="18sp" />
<ImageView
android:id="#+id/sound"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:paddingRight="8dp"
android:src="#drawable/speaker" />
</com.example.app.common.CheckedRelativeLayout>
my CheckedRelativeLayout:
package com.example.app.common;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Checkable;
import android.widget.RelativeLayout;
public class CheckedRelativeLayout extends RelativeLayout implements Checkable {
private static final int[] CHECKED_STATE_SET = {
android.R.attr.state_checked
};
private boolean checked = false;
#SuppressLint("NewApi")
public CheckedRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CheckedRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CheckedRelativeLayout(Context context) {
super(context);
}
#Override
public boolean isChecked() {
return checked;
}
#Override
public void setChecked(boolean checked) {
this.checked = checked;
refreshDrawableState();
//Propagate to childs
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if(child instanceof Checkable) {
((Checkable)child).setChecked(checked);
}
}
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
#Override
public void toggle() {
this.checked = !this.checked;
}
}
i have solved it: (it was a consequential error)
after my update 4.4.4 to 5.0.2 the Text-To-Speak was delete :( ..after manually installing the tts, the code work too. :)

Trouble getting video to play on Recyclerview

I have a VideoView inside a RecyclerView. I want to eventually have a list of videos to play on a Recyclerview. I decided to start out with one video, then move on to having multiple videos. I can't seem to get one video to play in the Recyclerview. When I start the app on my phone, all I get is the progress bar. The onPrepared function is never called for some reason. Here's my code.
RecyclerActivity.java
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.view.Menu;
import android.view.MenuItem;
import android.support.v7.widget.RecyclerView;
public class RecyclerActivity extends ActionBarActivity {
private RecyclerView mRecyclerView;
private RecyclerView.Adapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler);
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new MyAdapter(this);
mRecyclerView.setAdapter(mAdapter);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_recycler, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
MyAdapter.java
import android.app.ProgressDialog;
import android.content.Context;
import android.media.MediaPlayer;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.MediaController;
import android.widget.TextView;
import android.widget.VideoView;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private Context context;
private ProgressDialog progressDialog;
private MediaController mediaControls;
public static class ViewHolder extends RecyclerView.ViewHolder{
public VideoView mVideoView;
public ViewHolder(View v){
super(v);
mVideoView = (VideoView) v.findViewById(R.id.video_view);
}
}
public MyAdapter(Context mContext) {
context = mContext;
mediaControls = new MediaController(context);
progressDialog = new ProgressDialog(context);
progressDialog.setTitle("Random Video");
progressDialog.setMessage("Loading...");
progressDialog.setCancelable(false);
progressDialog.show();
}
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_video_view, parent,false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
try{
holder.mVideoView.setMediaController(mediaControls);
holder.mVideoView.setVideoURI(Uri.parse("android.resource://" + context.getPackageName() + "/" + R.raw.kitkat));
} catch (Exception e){
Log.e("Error", e.getMessage());
e.printStackTrace();
}
holder.mVideoView.requestFocus();
holder.mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
progressDialog.dismiss();
holder.mVideoView.start();
}
});
}
#Override
public int getItemCount() {
return 1;
}
my_video_view.xml
<FrameLayout 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:layout_gravity="center"
tools:context=".RecyclerActivity">
<VideoView
android:id="#+id/video_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>
activity_recycler.xml
<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:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin" tools:context=".RecyclerActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/my_recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
Any ideas? The video file is a .3gp file by the way. Thanks!
I was stuck on this for far too long as well. After searching everywhere and still struggling I looked at my older code base with the exact same code bar one difference. The VideoView was having its height programatically set. android:layout_height="wrap_content" doesn't appear to make a height adjustment.
So I used a self made utility method to get the screen width and set the height to be in a 16:9 ratio like so. (Maybe just set your height and width to some arbitrary numbers first to see if it's the issue)
public static int getScreenWidth(Context c) {
int screenWidth = 0; // this is part of the class not the method
if (screenWidth == 0) {
WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
screenWidth = size.x;
}
return screenWidth;
}
Then all it takes is to set the VideoViews height as such
holder.mVideoView.getLayoutParams().height = getScreenWidth(mContext) * 9 /16;
NOTE: My width is set to match_parent, so wrap_content may also fail there too.
Hope this helps.
the problem is with
android:layout_width="wrap_content"
android:layout_height="wrap_content"
before the video is loaded their values are 0 to solve this problem can use set minHeight to 1dp and layout_width to match_parent
and this will do the trick, you do not need fixed width and height.

Categories