I'd like to use the Template Method Pattern for drawing shapes GUI. Any suggestion as to what can be the task of the Template Method in the abstract class Shape? I mean, what can this method produce?
Thank you.
Draw method. Since every shape has different specification in rendering.
you can refer to the below example, i made comments in the code. hope it helps to you...
CustomShape.java - your abstract class
public abstract class CustomShape extends View {
int shapeType = 0;
int clr = Color.BLACK;
int x=0;
int y=0;
public CustomShape(Context context) {
super(context);
}
// OnDraw can act as Template Method
// This method holds the algorithm of shape creation
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#Override
final public void onDraw(Canvas canvas) {
super.onDraw(canvas);
// you can put here more method to make your shape different
// for example setColor(); setStroke() .....
createRectangle(canvas);
}
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Primitive operation sub classes must override
abstract void setShapeType(int type);
// Primitive operation sub classes must override
abstract void setShapeColor(int color);
// Primitive operation sub classes must override
abstract void setXY(int x1,int y1);
// Concreate Operation we dont want subclass to override
final void createRectangle(Canvas canvas) {
if (shapeType == 0) {
if (isColored()) {
canvas.drawRect(x, y, x+100, y+100, getPaint(clr, 1));
} else {
canvas.drawRect(x, y, x+100, y+100, getPaint(Color.BLACK, 1));
}
} else {
if (isColored()) {
canvas.drawCircle(x, y, 80, getPaint(clr, 1));
} else {
canvas.drawCircle(x, y, 80, getPaint(clr, 1));
}
}
}
// Concreate Operation we dont want subclass to override
final Paint getPaint(int color, int Stroke) {
Paint paint = new Paint();
paint.setColor(color);
paint.setStrokeWidth(Stroke);
return paint;
}
// HOOK - sub class can override but doesnt have to,
boolean isColored() {
return true;
}
}
CustomShape1.java - your concreate class
public class CustomShape1 extends CustomShape {
public CustomShape1(Context context) {
super(context);
}
boolean isColored(){
return true;
}
#Override
void setShapeType(int type) {
shapeType= type;
}
#Override
void setShapeColor(int color) {
clr = color;
}
#Override
void setXY(int x1, int y1) {
x = x1;
y =y1;
}
}
MainActivity.java
public class MainActivity extends Activity {
LinearLayout ln1,ln2;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ln1 = (LinearLayout)findViewById(R.id.ln1);
ln2= (LinearLayout)findViewById(R.id.ln2);
CustomShape1 cs1 = new CustomShape1(this);
cs1.setShapeType(1);
cs1.setShapeColor(Color.YELLOW);
cs1.setXY(100, 100);
CustomShape1 cs2 = new CustomShape1(this);
cs2.setShapeType(0);
cs2.setShapeColor(Color.RED);
cs2.setXY(300, 300);
ln2.addView(cs2);
ln1.addView(cs1);
}
}
activity_main.xml
<LinearLayout
android:id="#+id/ln1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
</LinearLayout>
<LinearLayout
android:id="#+id/ln2"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
</LinearLayout>
</RelativeLayout>
Related
Today I'm trying to use a LiveData (MutableLiveData) object to have some dynamics values. (in MVVM pattern)
I used a model object like this:
public class Object {
private String name;
private float internalvalue;
private float in1;
private float out1;
private float out2;
public Object(String name, float internalvalue){
this.name = name;
this.internalvalue = internalvalue;
}
public float getOut1(){
return this.out1;
}
public float getOut2(){
return this.out2;
}
public void setIn1(float in1){
this.in1 = in1;
}
private void performSomething(float internalvalue, float in1){
SubClassSingleton.performSomething(internalvalue, in1, new SubClassSingletonListener() {
#Override
public void onSuccess(float out1, float out2){
this.out1 = out1;
this.out2 = out2;
}
});
}
}
I use a ViewModel like this:
public class MainViewModel {
public MutableLiveData<Object> obj;
public MainViewModel(){
this.obj = new MutableLiveData<>();
this.obj.postValue(new Object("Name", 50.0f);
}
}
In MainFragment:
public class MainFragment extends Fragment {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
MainViewModel viewModel = ViewModelProviders.of(this).get(MainViewModel.class);
MainFragmentBinding binding = MainFragmentBinding.inflate(getLayoutInflater());
binding.setViewModel(viewModel);
binding.setLifecycleOwner(this);
return binding.getRoot();
}
}
And in View:
<layout ...
<data>
<variable
name="viewModel"
type="app.example.MainViewModel"/>
</data>
<LinearLayout
...>
<EditText
android:text="#={viewModel.obj.in1}"
.../>
<TextView
android:text="#{viewModel.obj.out1}"
.../>
<TextView
android:text="#{viewModel.obj.out2}"
.../>
</LinearLayout>
</layout>
I'd like to update my view when the values (out1, out2) of my model are updated (when the calculation is performed).
How can I do this ?
Try this:
You need to make your model class extend BaseObservable.
public class Object extends BaseObservable {
private String name;
private float internalvalue;
private float in1;
private float out1;
private float out2;
public Object(String name, float internalvalue) {
this.name = name;
this.internalvalue = internalvalue;
}
public float getOut1() {
return this.out1;
}
public void setOut1(float out1) {
this.out1 = out1;
notifyChange();
}
public float getOut2() {
return this.out2;
}
public void setOut2(float out2) {
this.out2 = out2;
notifyChange();
}
public void setIn1(float in1) {
this.in1 = in1;
}
private void performSomething(float internalvalue, float in1) {
SubClassSingleton.performSomething(internalvalue, in1, new SubClassSingletonListener() {
#Override
public void onSuccess(float out1, float out2) {
setOut1(out1);
setOut2(out2);
}
});
}
}
And in View, you cannot bind float directly to view. It has to be string, so bind like this:
<layout ...
<data>
<variable
name="viewModel"
type="app.example.MainViewModel"/>
</data>
<LinearLayout
...>
<EditText
android:text='#={""+viewModel.obj.in1}'
.../>
<TextView
android:text='#{""+viewModel.obj.out1}'
.../>
<TextView
android:text='#{""+viewModel.obj.out2}'
.../>
</LinearLayout>
</layout>
I want to achieve something like below, my list data is coming from a restful server. Everything is working except that the list is empty.
I used the following;
* Recyclerview Adaptor
* Created a pojo for list items, logs show that that the data is coming through from the server except that the list is not populated
//TransactionsFragment
public class TransactionsFragment extends Fragment {
View rootView;
RecyclerView recyclerView;
String transaction_amount;
String transaction_date_created;
public String authToken;
public TransactionsFragment() {
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_transactions_list, container, false);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
recyclerView = rootView.findViewById(R.id.rv_transactions);
recyclerView.setLayoutManager(linearLayoutManager);
TransactionListAdapter transactionListAdapter = new TransactionListAdapter(getContext(), getTransactions());
recyclerView.setAdapter(transactionListAdapter);
transactionListAdapter.notifyDataSetChanged();
return rootView;
}
private List<Transaction> getTransactions() {
List<Transaction> transactionList = new ArrayList<>();
/........
#Override
public void onNext(List<Transaction> transactions) {
Integer transactionSize = transactions.size();
for (int i =0;i<transactionSize;i++) {
transaction_amount = transactions.get(i).getAmount();
transaction_date_created = transactions.get(i).getDateCreated();
transactionList.add(new Transaction(transactions.get(i).getId(),null,transactions.get(i).getAmount(), transactions.get(i).getDateCreated(), transactions.get(i).getAccount()));
}
/// Data is shown here after running the app so it tells me that we are getting response from the server
Toast.makeText(getActivity(), "Transaction Date Created: " + transaction_date_created + "!", Toast.LENGTH_LONG).show();
}
#Override
public void onError(Throwable e) {
if (e instanceof HttpException) {
HttpException response = (HttpException) e;
int code = response.code();
String msg = response.message();
Toast.makeText(getActivity(), "Transaction Error message: " + msg + "!", Toast.LENGTH_LONG).show();
}
}
#Override
public void onComplete() {
}
});
//...
return transactionList;
}
}
//..TransactionListAdapter
public class TransactionListAdapter extends RecyclerView.Adapter {
private List transactionList;
private Context mContext;
public TransactionListAdapter(Context context, List<Transaction> transactions) {
this.mContext = context;
this.transactionList = transactions;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
RecyclerView.ViewHolder vh;
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fragment_transactions_single, parent, false);
vh = new OriginalViewHolder(view);
return vh;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int i) {
if (holder instanceof TransactionListAdapter.OriginalViewHolder) {
TransactionListAdapter.OriginalViewHolder view = (TransactionListAdapter.OriginalViewHolder) holder;
Transaction transaction = transactionList.get(i);
view.tvTransactionAmount.setText(transaction.getAmount());
view.tvTransactionDateCreated.setText(transaction.getDateCreated());
}
}
#Override
public int getItemCount() {
return transactionList.size();
}
public class OriginalViewHolder extends RecyclerView.ViewHolder{
ImageView imgTransactionType;
TextView tvTransactionName, tvTransactionAmount, tvTransactionDateCreated, tvDefaultCurrency, getTvTransactionUSD;
public OriginalViewHolder(View itemView) {
super(itemView);
tvTransactionDateCreated = itemView.findViewById(R.id.tv_transaction_date_created);
tvTransactionAmount = itemView.findViewById(R.id.tv_transaction_amount);
}
}
}
//..MainActivity
public class MainActivity extends AppCompatActivity implements
View parent_view;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
checkConnection();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
setContentView(R.layout.activity_home);
initiateViews();
Fragment transFragment = new TransactionsFragment();
final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.transactions_frame_container, transFragment);
transaction.addToBackStack(null);
transaction.commit();
}
private void initiateViews() {
parent_view = (CoordinatorLayout)findViewById(R.id.home_container);
home_container_id = R.id.frame_container;
}
private void loadFragment(Fragment fragment, Integer container_id) {
// create a FragmentManager
FragmentManager fm = getSupportFragmentManager();
// create a FragmentTransaction to begin the transaction and replace the Fragment
FragmentTransaction fragmentTransaction = fm.beginTransaction();
// replace the FrameLayout with new Fragment
fragmentTransaction.replace(container_id, fragment);
fragmentTransaction.commit(); // save the changes
}
}
//..fragment_transactions_list.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#color/grey_10" />
<View
android:layout_width="0dp"
android:layout_height="#dimen/spacing_middle" />
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/rv_transactions"
android:scrollbars="vertical"
android:scrollingCache="true"/>
</LinearLayout>
I am trying to learn ViewModel in android, in my first phase of learning I am trying to update UI (TextView) by using ViewModel and DataBinding. In ViewModel, I have an AsyncTask callback and it will invoke REST API call. I am getting the response from API call but the value in textview is not getting updated.
my ViewModel class:
public class ViewModelData extends ViewModel {
private MutableLiveData<UserData> users;
public LiveData<UserData> getUsers() {
if (users == null) {
users = new MutableLiveData<UserData>();
loadUsers();
}
return users;
}
public void loadUsers() {
ListTask listTask =new ListTask (taskHandler);
listTask .execute();
}
public Handler taskHandler= new Handler() {
#Override
public void handleMessage(Message msg) {
UserData userData = (UserData) msg.obj;
users.setValue(userData);
}
};
}
and my MainActivity class:
public class MainActivity extends AppCompatActivity implements LifecycleOwner {
private LifecycleRegistry mLifecycleRegistry;
private TextView fName;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fName = (TextView)findViewById(R.id.text_name);
mLifecycleRegistry = new LifecycleRegistry(this);
mLifecycleRegistry.markState(Lifecycle.State.CREATED);
ViewModelData model = ViewModelProviders.of(this).get(ViewModelData.class);
model.getUsers().observe(this, new Observer<UserData>() {
#Override
public void onChanged(#Nullable UserData userData) {
Log.d("data"," = - - - - ="+userData.getFirstName());
}
});
}
#Override
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
}
and my data class:
public class UserData extends BaseObservable{
private String firstName ;
#Bindable
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
}
and layout file
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="android.view.View" />
<variable name="data" type="com.cgi.viewmodelexample.UserData"/>
</data>
<RelativeLayout
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
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="com.cgi.viewmodelexample.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{data.firstName}"
android:id="#+id/text_name"/>
</RelativeLayout>
</layout>
I suggest to follow next basic principles:
don't overload data objects by business or presentation logic
only view model required to obtain data in presentation layer
view model should expose only ready to use data to presentation layer
(optional) background task should expose LiveData to deliver data
Implementation notes:
firstName is read only on view
lastName is editable on view
loadUser() is not threadsafe
we have error message when call save() method until data is not loaded
Don't overload data objects by business or presentation logic
Suppose, we have UserData object with first and last name. So, getters it's (usually) all what we need:
public class UserData {
private String firstName;
private String lastName;
public UserData(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
Only view model required to obtain data in presentation
To follow this suggestion we should to use only view model in data binding layout:
<?xml version="1.0" encoding="utf-8"?>
<layout 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"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="com.example.vmtestapplication.MainActivity">
<data>
<import type="android.view.View" />
<!-- Only view model required -->
<variable
name="vm"
type="com.example.vmtestapplication.UserDataViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical">
<!-- Primitive error message -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{vm.error}"
android:visibility="#{vm.error == null ? View.GONE : View.VISIBLE}"/>
<!-- Read only field (only `#`) -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{vm.firstName}" />
<!-- Two-way data binding (`#=`) -->
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#={vm.lastName}" />
</LinearLayout>
</layout>
Note: you can use a few view models in one layout, but not raw data
View model should expose only ready to use data to presentation
This mean, you shouldn't to expose complex data objects (UserData in our case) directly from view model. Preferable to expose privative types which view can use as-is. In example below we don't need to hold UserData object because it used only to loading grouped data. We, probably, need to create UserData to save it but it depends on your repository implementation.
public class UserDataViewModel extends ViewModel {
private ListTask loadTask;
private final MutableLiveData<String> firstName = new MediatorLiveData<>();
private final MutableLiveData<String> lastName = new MediatorLiveData<>();
private final MutableLiveData<String> error = new MutableLiveData<>();
/**
* Expose LiveData if you do not use two-way data binding
*/
public LiveData<String> getFirstName() {
return firstName;
}
/**
* Expose MutableLiveData to use two-way data binding
*/
public MutableLiveData<String> getLastName() {
return lastName;
}
public LiveData<String> getError() {
return error;
}
#MainThread
public void loadUser(String userId) {
// cancel previous running task
cancelLoadTask();
loadTask = new ListTask();
Observer<UserData> observer = new Observer<UserData>() {
#Override
public void onChanged(#Nullable UserData userData) {
// transform and deliver data to observers
firstName.setValue(userData == null? null : userData.getFirstName());
lastName.setValue(userData == null? null : userData.getLastName());
// remove subscription on complete
loadTask.getUserData().removeObserver(this);
}
};
// it can be replaced to observe() if LifeCycleOwner is passed as argument
loadTask.getUserData().observeForever(observer);
// start loading task
loadTask.execute(userId);
}
public void save() {
// clear previous error message
error.setValue(null);
String fName = firstName.getValue(), lName = lastName.getValue();
// validate data (in background)
if (fName == null || lName == null) {
error.setValue("Opps! Data is invalid");
return;
}
// create and save object
UserData newData = new UserData(fName, lName);
// ...
}
#Override
protected void onCleared() {
super.onCleared();
cancelLoadTask();
}
private void cancelLoadTask() {
if (loadTask != null)
loadTask.cancel(true);
loadTask = null;
}
}
Background task should expose LiveData to deliver data
public class ListTask extends AsyncTask<String, Void, UserData> {
private final MutableLiveData<UserData> data= new MediatorLiveData<>();
public LiveData<UserData> getUserData() {
return data;
}
#Override
protected void onPostExecute(UserData userData) {
data.setValue(userData);
}
#Override
protected UserData doInBackground(String[] userId) {
// some id validations
return loadRemoiteUser(userId[0]);
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
private UserDataViewModel viewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get view model
viewModel = ViewModelProviders.of(this).get(UserDataViewModel.class);
// create binding
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
// set view model to data binding
binding.setVm(viewModel);
// don't forget to set LifecycleOwner to data binding
binding.setLifecycleOwner(this);
// start user loading (if necessary)
viewModel.loadUser("user_id");
// ...
}
}
PS: try to use RxJava library instead of AsyncTask to perform background work.
You'll require to notify observer when you set value like this :
public class UserData extends BaseObservable{
private String firstName ;
#Bindable
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName) // call like this
}
}
If you want binding layout to work then you have to set your view in binding way. Also set data in binding class.
public class MainActivity extends AppCompatActivity implements LifecycleOwner {
ActivityMainBinding binding;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
...
ViewModelData model = ViewModelProviders.of(this).get(ViewModelData.class);
...
binding.setData(model.getUsers());
}
}
I used to do my drawing in an ImageView in the onDraw method.
However, I've learnt that's better to draw the canvas outside of the onDraw and just update the canvas in onDraw.
I know this is clearly wrong (because it's not working) but how would I accomplish what I'm trying to do:
#Override
public void onDraw(Canvas c) {
c = this.newCanvas;
super.onDraw(c);
}
public class GameLoopThread extends Thread {
private GameView view;
private boolean running = false;
public GameLoopThread(GameView view) {
this.view = view;
}
public void setRunning(boolean run) {
running = run;
}
#Override
public void run() {
while (running) {
Canvas c = null;
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
if (c != null) {
view.onDraw(c);
}
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
try {
sleep(10);
} catch (Exception e) {}
}
}
}
make that thread then in your activity do something like this
#Override
public void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
setContentView(new GameView(GameActivity.this));
}
then in a GameViewClass do something like this
public class GameView extends SurfaceView {
private SurfaceHolder holder;
private GameLoopThread gameLoopThread;
public GameView(Context context) {
super(context);
gameLoopThread = new GameLoopThread(this);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
gameLoopThread.setRunning(false);
while (retry) {
try {
gameLoopThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
}
#Override
protected void onDraw(Canvas canvas) {
//Do Drawing
}
}
The important things here is that the thread is manually auto calling the onDraw() method repeatedly, and that you are locking a canvas, drawing on it, and then posting it. If you dont need a super fast refresh rate then you might be better off doing something like this:
#Override
public void onDraw(Canvas c) {
c = this.getHolder().lockCanvas();
if (c != null) {
//draw on canvas
}
if (c != null) {
this.getHolder().unlockCanvasAndPost(c);
}
}
I just dont know if that last bit there will work, never tested it.
also if you want to do your drawing outside the on draw method, you could run your updating (drawing on your canvas) in a thread, and every time the onDraw method is called have it check to see if the Canvas is ready for it to post. for example have your thread have a boolean that once the canvas gets pulled it is set to false, so the thread will draw you a new one, but once it is done drawing set the boolean to true. in the ondraw method check to see if the boolean is true and if it is pull the canvas.
A Canvas is just a handle for drawing onto something -- you need to get at the something itself. The Canvas that you draw into outside of onDraw() needs to be backed by a Bitmap. Then in onDraw(), simply draw that Bitmap into the Canvas provided:
Bitmap my_bitmap = null; /* this needs to be initialized whereever it is drawn into */
#Override
public void onDraw(Canvas c) {
if (my_bitmap != null) {
c.drawBitmap(my_bitmap, 0.0, 0.0, null);
}
}
onSizeChanged() would be a reasonable place to initialize the Bitmap, because then you know its size:
#Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
my_bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
}
And to draw on my_bitmap, just make a new Canvas with:
Canvas c = new Canvas(my_bitmap);
I found a tutorial and it looks like this:
package com.djrobotfreak.SVTest;
public class Tutorial2D extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new Panel(this));
}
class Panel extends SurfaceView implements SurfaceHolder.Callback {
private TutorialThread _thread;
public Panel(Context context) {
super(context);
getHolder().addCallback(this);
_thread = new TutorialThread(getHolder(), this);
}
#Override
public void onDraw(Canvas canvas) {
Bitmap _scratch = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(_scratch, 10, 10, null);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
_thread.setRunning(true);
_thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// simply copied from sample application LunarLander:
// we have to tell thread to shut down & wait for it to finish, or else
// it might touch the Surface after we return and explode
boolean retry = true;
_thread.setRunning(false);
while (retry) {
try {
_thread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
}
class TutorialThread extends Thread {
private SurfaceHolder _surfaceHolder;
private Panel _panel;
private boolean _run = false;
public TutorialThread(SurfaceHolder surfaceHolder, Panel panel) {
_surfaceHolder = surfaceHolder;
_panel = panel;
}
public void setRunning(boolean run) {
_run = run;
}
#Override
public void run() {
Canvas c;
while (_run) {
c = null;
try {
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
_panel.onDraw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
}
and it does not work, no matter what I do. I am trying to convert my code to surfaceview but I cant find any surfaceview programs that even work (besides the android-provided ones). Does anyone know what the error even is saying?
Here is my logcat info: http://shrib.com/oJB5Bxqs
If you get a ClassNotFoundException, you should check the Manifest file.
Click on the Application tab and look on the botton right side under "Attributes for".
If there is a red X mark under your Class Name, then click on the "Name" link and locate the correct class to load.