setContentView sets display black - java

There will be a lot of code. I had to leave it for you to understand logic of an application.
Here is the MainActivity. Called on starting.
public class MainActivity extends AppCompatActivity {
private GameView gameView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//some unnecessary code
setContentView(R.layout.activity_main);
}
public void startSurvival(View view) {
gameView = new GameView(this, this, "survival");
setContentView(gameView);
}
public void chooseData(View view){
setContentView(new DView(this, this));
}
public void backToMenu(){
setContentView(R.layout.activity_main);
gameView = null;
}
#Override
protected void onResume() {
super.onResume();
try {
gameView.update();
} catch (NullPointerException e) {}
}
}
This Activity is a List of options. You choose one and then GameView sets as content View with appropriate parameters.
Here is no questions so I cut almost all the code.
public class DView extends ListView {
DView(final Context context, final MainActivity mainActivity){
super(context);
this.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String[] columns = {"data"};
String having = "id = " + ids[position];
SQLiteDatabase db = dbHelper.getWritableDatabase();
Cursor cursor = db.query("levels", columns, null, null, ID, having, null);
if (cursor.moveToFirst()){
int dataInd = cursor.getColumnIndex(DATA);
mainActivity.setContentView(new GameView(context, mainActivity, cursor.getString(dataInd)));
}//everything here works fine. This just shows that setContentView can be done multiple times
//without bugs
cursor.close();
dbHelper.close();
}
});
}
}
And here comes the problem. When win() method is called display turns black. Application does not crash.
public class GameView extends SurfaceView{
public MainActivity mainActivity;
GameThread gameThread;
public Player player = null;
public Canvas canvas;
public ExtraData data;
public GameView (Context context, MainActivity mainActivity, String data){
super(context);
this.mainActivity = mainActivity;
if (data.equals("survival")) {
this.data = new ExtraData("RandomSpawn47",null, this);
} else {
this.data = new ExtraData("UsingData", data, this);
}
update();
}
void update(){
gameThread = new GameThread(this);
getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
gameThread.running(true);
if (gameThread.getState() == Thread.State.NEW)
gameThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
gameThread.running(false);
}
});
if (player == null)
player = new Player(this);
}
public class GameThread extends Thread{
private GameView gameView;
public GameThread(GameView gameView) {
this.gameView = gameView;
}
public void running(boolean run){
running = run;
}
#Override
public void run() {
while (running){
canvas = null;
try{
canvas = gameView.getHolder().lockCanvas(null);
synchronized (gameView.getHolder()){
draw(canvas);
this.wait(45);
}
} catch(Exception e) {}
finally {
if((canvas != null)&&(running)){
gameView.getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
canvas.drawColor(Color.BLUE);
data.onDraw();
}
public void win(){
mainActivity.backToMenu();//not switching the menu
}
}
Other classes like ExtraData and Player are not important.
GameThread and SurfaceView destroyes (I checked them with Logs in onDestroy() and in the end of run() method).

You are calling method of Activity from another class. Instead of mainActivity.backToMenu(), create one interface, implement it in MainActivity and pass the reference in GameView to initialize the interface. And where you are calling win method, call interface method instead of calling the public method of MainActivity.
Create one interface like:
public interface UpdateActivity{
void updateActivity();
}
In MainActivity
public class MainActivity extends AppCompatActivity implements UpdateActivity
then override the method of interface
void updateActivity(){
setContentView(R.layout.activity_main);
gameView = null;
}
Pass the reference in GameView instead of passing the MainActivity reference.
public GameView (Context context, UpdateActivity updateActivity , String data) {
super(context);
this.updateActivity = updateActivity ;
if (data.equals("survival")) {
this.data = new ExtraData("RandomSpawn47",null, this);
} else {
this.data = new ExtraData("UsingData", data, this);
}
update();
}
Now call the method where you want to call:
updateActivity.updateActivity();

Related

How do I update a view out of an AsyncTask?

I have to create a RecyclerView which is updated every time a new item is created by my AsyncTask. So the RecyclerView is building itself up gradually.
Every Item is generated and then the Thread sleeps for a time to see the progress slower.
I tried to get the Adapter and update it with notifyDataSetChanged(), but it wont work like this. The Error I get is:
Only the original thread that created a view hierarchy can touch its
views.
Another idea was to update the adapter in my MainActivity with the use of a interface. But I dont exactly know how to do that. First I have to know if its the right way to use an interface or if there is a better way or maybe a really easy solution for my problem.
public class MainActivity extends AppCompatActivity implements ListAdapter.Listener{
static RecyclerView recyclerView;
static ListAdapter adapter;
#Override
public void click(String name) {
if(getResources().getConfiguration().orientation== Configuration.ORIENTATION_LANDSCAPE){
DetailsFragment detailsFragment = (DetailsFragment)getSupportFragmentManager().findFragmentById(R.id.fragment_details);
detailsFragment.setData(name);
}
else{
Toast.makeText(this, "clicked", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(this, DetailsActivity.class);
intent.putExtra("sorte_name", name);
startActivity(intent);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initRecyclerView();
new DataContainer().execute();
}
private void initRecyclerView(){
recyclerView = findViewById(R.id.meinRecyclerView);
adapter = new ListAdapter(this, DataContainer.meineSortenListe, this);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}
}
public class DataContainer extends AsyncTask<Void, Void, Void> {
public static ArrayList<String> meineSortenListe = new ArrayList<String>();
ListAdapter myAdapter;
#Override
protected void onPreExecute() {
myAdapter =(ListAdapter)MainActivity.recyclerView.getAdapter();
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... voids) {
for(int i= 0; i<50; i++){
meineSortenListe.add("Sorte "+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
onProgressUpdate();
}
Log.i("info", "array befüllt");
return null;
}
#Override
protected void onProgressUpdate(Void... values) {
myAdapter.notifyDataSetChanged();
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
}
public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ViewHolder>{
private ArrayList<String> mSorten;
private Context mContext;
private Listener listener;
public interface Listener{
void click(String name);
}
//Constructor
public ListAdapter(Context mContext, ArrayList<String> sortenListe, Listener listener) {
this.mContext = mContext;
this.mSorten = sortenListe;
this.listener=listener;
}
///////////////////////
public class ViewHolder extends RecyclerView.ViewHolder {
TextView sortenName;
LinearLayout sorteLayout;
public ViewHolder(View itemView) {
super(itemView);
sortenName = itemView.findViewById(R.id.nameSorte);
sorteLayout = itemView.findViewById(R.id.sorteLayout);
}
}
////////////////////////////////
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, final int position) {
holder.sortenName.setText(mSorten.get(position));
holder.sorteLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
listener.click(mSorten.get(position));
}
});
}
#Override
public int getItemCount() {
return mSorten.size();
}
}
In Android, only the UI thread can update views. (It is possible to trigger UI updates in onPostExecute, since onPostExecute is switching to the UI thread.)
Since you are still in the doInBackground() function, you need a Handler to send a Runnable with your code to the UI thread's MessageQueue.
So in your DataContainer, change onProgressUpdate to
protected void onProgressUpdate(Void... values) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable(){
public void run() {
myAdapter.notifyDataSetChanged();
}
});
super.onProgressUpdate(values);
}

Extract some value in click listener from outer class

I have a custom class which has a unique interface
public class CalculatorDialog extends Dialog implements OnClickListener {
TextView mView;
CalculatorListener delegate = null;
public CalculatorDialog (Context context, CalculatorListener delegate) {
this.context = context;
this.delegate = delegate;
}
public interface CalculatorListener extends OnClickListener {
#Override void onClick(View v);
}
#Override void onCreate(Bundle bundle) {
...
mView = (TextView) findViewById(...);
findViewbyId(...Button...).setOnClickListener(delegate);
}
public String getViewText() {
mView.getText().toString();
}
When creating new object of CalculatorDialog I want to implement my own action for clicking Button, but I want to get some String from a visible text view.
So in my MainActivity I try to do this:
CalculatorDialog dialogBox = new CalculatorDialog(context, new CalculatorDialog.CalculatorListener() {}
#Override
public void onClick(View v) {
String test = getViewText();
}
});
But as you and I have thought it can't be accessed from there.
Code here is not 1:1 with what I have in my project, but I think it represenets my needs. Also I have wrote it directly on StackOverflow, so it may contain some code bugs.
How can I access this function?
You should change your CalculatorListener
public class CalculatorDialog extends Dialog implements OnClickListener {
TextView mView;
CalculatorListener delegate = null;
public CalculatorDialog (Context context, CalculatorListener delegate) {
this.context = context;
this.delegate = delegate;
}
public interface CalculatorListener{
void onClick(View v, String text);
}
#Override void onCreate(Bundle bundle) {
...
mView = (TextView) findViewById(...);
findViewbyId(...Button...).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
delegate.onClick(view, getViewText());
});
}
public String getViewText() {
mView.getText().toString();
}
and
CalculatorDialog dialogBox = new CalculatorDialog(context, new CalculatorDialog.CalculatorListener() {}
#Override
public void onClick(View v, String text) {
String test = text;
}

Thread class and pass information to main activity class into TextView

I would like to make method onSenzorChange run into thread. To run more smooth. And get information of x axis everytime change. And pass it to main class (Activity) into TextView.
MainActivity class :
public class MainActivity extends AppCompatActivity {
TextView textView;
TestOfPassUIThread t;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
t = new TestOfPassUIThread(this);
}
public void onStart(View view) {
t.register();
}
}
TestOfPassUIThread class ( not an activity or anything )
public class TestOfPassUIThread implements SensorEventListener {
private SensorManager sensorManager;
private Sensor sensor;
public TestOfPassUIThread (Context context) {
sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
public void register(){
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
}
public void unregister() {
sensorManager.unregisterListener(this);
}
// Want this method be in Thread
//How can I do this ?
#Override
public void onSensorChanged(SensorEvent event) {
float xAxis = event.values[0];
// And I want it to display in TextView!
// In main activity would be textView.setText("" + xAxis);
//How to pass it to MainActivity class ?
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
There are multiple ways to achieve writing on the textview, the following is one way to do it. (you may wanna read about callbacks, check How to implement callbacks in Java).
As for accessing the UI thread from the background, there multiple ways to do it as well (check: Running code in main thread from another thread).
For why we used a HandlerThread below, you can read about it here: Acclerometer Sensor in Separate Thread.
So your listener becomes:
public abstract class TestOfPassUIThread implements SensorEventListener {
private SensorManager sensorManager;
private Sensor sensor;
private HandlerThread handlerThread;
private Context context;
private Runnable uiRunnable;
private float xAxis;
public TestOfPassUIThread (Context context) {
this.context = context;
sensorManager = (SensorManager) context.getSystemService (Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor (Sensor.TYPE_ACCELEROMETER);
}
public void register () {
initUiRunnable ();
handlerThread = new HandlerThread ("sensorHandler");
handlerThread.start ();
sensorManager.registerListener (
this,
sensor,
SensorManager.SENSOR_DELAY_NORMAL,
new Handler (handlerThread.getLooper ())
);
}
public void unregister () {
sensorManager.unregisterListener (this);
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
handlerThread.quitSafely ();
return;
}
handlerThread.quit ();
} finally {
uiRunnable = null;
}
}
#Override
public void onSensorChanged (SensorEvent event) {
xAxis = event.values [0];
// your other background operations
((Activity)context).runOnUiThread (uiRunnable);
// your other background operations
}
#Override
public void onAccuracyChanged (Sensor sensor, int accuracy) {
}
private void initUiRunnable () {
uiRunnable = new Runnable () {
#Override
public void run () {
// ...... your other UI operations
fillTextView (xAxis);
// ...... your other UI operations
}
};
}
public abstract void fillTextView (float xAxis);
}
And your activity:
public class MainActivity extends AppCompatActivity {
private TextView textView;
private TestOfPassUIThread t;
#Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_main);
textView = (TextView)findViewById (R.id.textView);
t = new TestOfPassUIThread (this) {
#Override
public void fillTextView (float xAxis) {
textView.setText ("Current xAxis: " + xAxis);
}
};
}
#Override
protected void onResume () {
super.onResume ();
t.register ();
}
#Override
protected void onPause () {
t.unregister ();
super.onPause ();
}
}
Also, when you override LifeCycle methods of Activities such as onStart, onResume etc..., make sure to call the super.lifeCycleMethod.

how to work with the loader when the screen is rotated

I try to use Loaders instead AsyncTask.I can not figure out how to correctly organize the conservation status when the screen is rotated. I receive information from Json and trying create list.
public class FragmentWeatherTestApplication extends Fragment
implements LoaderManager.LoaderCallbacks<List<WeatherItem>> {
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
getLoaderManager().initLoader(0,null,this).forceLoad();
}
...
#Override
public Loader<List<WeatherItem>> onCreateLoader(int id, Bundle args) {
Log.i(TAG, "onCreateLoader");
return new FetchWeatherLoader(getActivity());
}
#Override
public void onLoadFinished(Loader<List<WeatherItem>> loader, List<WeatherItem> data) {
Log.i(TAG, "onLoadFinished");
mItems = data;
setupAdapter();
}
#Override
public void onLoaderReset(Loader<List<WeatherItem>> loader) {
Log.i(TAG, "onLoaderReset");
}
....
private static class FetchWeatherLoader extends AsyncTaskLoader<List<WeatherItem>>{
public FetchWeatherLoader(Context context) {
super(context);
}
#Override
public List<WeatherItem> loadInBackground() {
return new OpenWeatherFetch().fetchItems();
}
}

Setting up MediaController

Im trying to set up the MediaController so I can have controls when playing back audio but when I try to declare it the "this" is coming up as an error. What am I doing wrong?
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class MainActivity extends AppCompatActivity implements MediaRecorder.OnInfoListener {
private boolean cont;
private MediaRecorder mediaRecorder;
private MediaPlayer mediaPlayer;
private String OUTPUT_FILE;
private MediaController mediaController;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
OUTPUT_FILE = Environment.getExternalStorageDirectory()+"/androidaudio1.3gpp";
cont=true;
}
public void buttonClicked(View view){
switch (view.getId()){
case R.id.startRec:
beginRecord();
break;
case R.id.stopRec:
stopRecord();
break;
case R.id.startPlay:
try {
begginPlaying();
} catch (IOException e) {
e.printStackTrace();
}
break;
case R.id.stopPlay:
stopPlaying();
break;
}
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void begginPlaying() throws IOException {
if (mediaPlayer != null)
mediaPlayer.release();
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(OUTPUT_FILE);
mediaController = new MediaController(this);
mediaController.setMediaPlayer(this);
mediaController.show();
mediaPlayer.prepare();
mediaPlayer.start();
}
private void stopPlaying(){
if (mediaPlayer!= null){
mediaPlayer.stop();
mediaPlayer.reset();
mediaPlayer.release();
mediaPlayer=null;
}
#Override
public void start() {
mediaPlayer.start();
}
#Override
public void pause() {
mediaPlayer.pause();
}
#Override
public int getDuration() {
return mediaPlayer.getDuration();
}
#Override
public int getCurrentPosition() {
return mediaPlayer.getCurrentPosition();
}
#Override
public void seekTo(int pos) {
mediaPlayer.seekTo(pos);
}
#Override
public boolean isPlaying() {
return mediaPlayer.isPlaying();
}
#Override
public int getBufferPercentage() {
return 0;
}
#Override
public boolean canPause() {
return true;
}
#Override
public boolean canSeekBackward() {
return true;
}
#Override
public boolean canSeekForward() {
return true;
}
#Override
public int getAudioSessionId() {
return 0;
}
}
MediaController requires the context to be passed to the constructor. Without anymore code or knowing where you declared the MediaController, my best guess is that you don't have access to the context in the place where you declared the MediaController. Either pass the context from the activity where you would like to use the MediaController, or put this code in that activity.
https://developer.android.com/reference/android/widget/MediaController.html
EDIT:
I tried your code, check your imports & ensure you have, import android.widget.MediaController & not import android.media.session.MediaController.
Try this
if you are using in Activity then you can pass this
if you are using in Fragment then use getActivity() or context
and to set MediaController
use this code
video.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.setOnVideoSizeChangedListener(new OnVideoSizeChangedListener() {
#Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
/*
* add media controller
*/
mc = new MediaController(YourActivity.this);
video.setMediaController(mc);
/*
* and set its position on screen
*/
mc.setAnchorView(video);
}
});
}
});

Categories

Resources