I am trying to build a card based game in which a card is added to player after every round. To win the match player has to remove all cards from his deck.However i have also drawn some additional button for some other purposes. The issue is that when the number of cards got high, they hide behind the buttons like below image.
Is there any way through which i can draw the bitmaps in a fixed area? Something like below image(Highlighted rectangle behind the card images).
Following is my code to draw Bitmaps for one player
private void setMainPlayer() {
// Log.d(TAG, "Inside Set Main Player Method");
Card localcard = null;
Bitmap localimage = null;
int currentiteration = 0;
int Card_Gap = Screen_Width / 10;
int Down_Card_Gap = 0;
int Down_Card_Gap_positive = 0;
int Down_Card_Gap_negative = 0;
playerList.get(0).sortBySuit();
// Log.d(TAG,"Main Player Deck size"+MainPlayer.Count());
while (currentiteration < playerList.get(0).decksize()) {
localcard = playerList.get(0).getCard(currentiteration);
localcard.setCurrent_Y(Screen_Height - localcard.getImage(context, Card_Width, Card_Height).getHeight());
playerList.get(0).setCurrentCard(localcard, currentiteration);
currentiteration++;
if (Down_Card_Gap >= 0) {
Down_Card_Gap_positive = Down_Card_Gap;
localcard.setCurrent_X(Screen_Center_X + Down_Card_Gap_positive);
Down_Card_Gap += Card_Gap;
} else {
Down_Card_Gap_negative = Down_Card_Gap;
localcard.setCurrent_X(Screen_Center_X + Down_Card_Gap_negative);
}
Down_Card_Gap *= -1;
}
}
private void DrawMainPlayerDeck(Canvas canvas) {
// Log.d(TAG, " Inside Draw Main Player Deck");
Card localcard;
int currentiteration = 0;
while (currentiteration < playerList.get(0).decksize()) {
localcard = playerList.get(0).getCard(currentiteration);
canvas.drawBitmap(localcard.getImage(context, Card_Width, Card_Height), localcard.getCurrent_X(), localcard.getCurrent_Y(), null);
currentiteration++;
}
}
I am drawing bitmaps on surfaceview.
Any suggestion would be really helpful.
Thanks in advance.
Related
I am making one music application in android.In this music list coming from server side. I don'tknow how to show waveform of audio in android ? like in soundcloud website. I have attached image below.
Perhaps, you can implements this feature without libraries, of course if you want only visualisation of audio sample.
For example:
public class PlayerVisualizerView extends View {
/**
* constant value for Height of the bar
*/
public static final int VISUALIZER_HEIGHT = 28;
/**
* bytes array converted from file.
*/
private byte[] bytes;
/**
* Percentage of audio sample scale
* Should updated dynamically while audioPlayer is played
*/
private float denseness;
/**
* Canvas painting for sample scale, filling played part of audio sample
*/
private Paint playedStatePainting = new Paint();
/**
* Canvas painting for sample scale, filling not played part of audio sample
*/
private Paint notPlayedStatePainting = new Paint();
private int width;
private int height;
public PlayerVisualizerView(Context context) {
super(context);
init();
}
public PlayerVisualizerView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
bytes = null;
playedStatePainting.setStrokeWidth(1f);
playedStatePainting.setAntiAlias(true);
playedStatePainting.setColor(ContextCompat.getColor(getContext(), R.color.gray));
notPlayedStatePainting.setStrokeWidth(1f);
notPlayedStatePainting.setAntiAlias(true);
notPlayedStatePainting.setColor(ContextCompat.getColor(getContext(), R.color.colorAccent));
}
/**
* update and redraw Visualizer view
*/
public void updateVisualizer(byte[] bytes) {
this.bytes = bytes;
invalidate();
}
/**
* Update player percent. 0 - file not played, 1 - full played
*
* #param percent
*/
public void updatePlayerPercent(float percent) {
denseness = (int) Math.ceil(width * percent);
if (denseness < 0) {
denseness = 0;
} else if (denseness > width) {
denseness = width;
}
invalidate();
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
width = getMeasuredWidth();
height = getMeasuredHeight();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (bytes == null || width == 0) {
return;
}
float totalBarsCount = width / dp(3);
if (totalBarsCount <= 0.1f) {
return;
}
byte value;
int samplesCount = (bytes.length * 8 / 5);
float samplesPerBar = samplesCount / totalBarsCount;
float barCounter = 0;
int nextBarNum = 0;
int y = (height - dp(VISUALIZER_HEIGHT)) / 2;
int barNum = 0;
int lastBarNum;
int drawBarCount;
for (int a = 0; a < samplesCount; a++) {
if (a != nextBarNum) {
continue;
}
drawBarCount = 0;
lastBarNum = nextBarNum;
while (lastBarNum == nextBarNum) {
barCounter += samplesPerBar;
nextBarNum = (int) barCounter;
drawBarCount++;
}
int bitPointer = a * 5;
int byteNum = bitPointer / Byte.SIZE;
int byteBitOffset = bitPointer - byteNum * Byte.SIZE;
int currentByteCount = Byte.SIZE - byteBitOffset;
int nextByteRest = 5 - currentByteCount;
value = (byte) ((bytes[byteNum] >> byteBitOffset) & ((2 << (Math.min(5, currentByteCount) - 1)) - 1));
if (nextByteRest > 0) {
value <<= nextByteRest;
value |= bytes[byteNum + 1] & ((2 << (nextByteRest - 1)) - 1);
}
for (int b = 0; b < drawBarCount; b++) {
int x = barNum * dp(3);
float left = x;
float top = y + dp(VISUALIZER_HEIGHT - Math.max(1, VISUALIZER_HEIGHT * value / 31.0f));
float right = x + dp(2);
float bottom = y + dp(VISUALIZER_HEIGHT);
if (x < denseness && x + dp(2) < denseness) {
canvas.drawRect(left, top, right, bottom, notPlayedStatePainting);
} else {
canvas.drawRect(left, top, right, bottom, playedStatePainting);
if (x < denseness) {
canvas.drawRect(left, top, right, bottom, notPlayedStatePainting);
}
}
barNum++;
}
}
}
public int dp(float value) {
if (value == 0) {
return 0;
}
return (int) Math.ceil(getContext().getResources().getDisplayMetrics().density * value);
}
}
Sorry, code with a small amount of comments, but it is working visualizer. You can attach it to any players you want.
How you can use it: add this view in your xml layout, then you have to update visualizer state with methods
public void updateVisualizer(byte[] bytes) {
playerVisualizerView.updateVisualizer(bytes);
}
public void updatePlayerProgress(float percent) {
playerVisualizerView.updatePlayerPercent(percent);
}
In updateVisualizer you pass bytes array with you audio sample, and in updatePlayerProgress you dynamically pass percentage, while audio sample is playing.
for converting file to bytes you can use this helper method
public static byte[] fileToBytes(File file) {
int size = (int) file.length();
byte[] bytes = new byte[size];
try {
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file));
buf.read(bytes, 0, bytes.length);
buf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return bytes;
}
and for example(very shortly), how it looks like with Mosby library:
public class AudioRecorderPresenter extends MvpBasePresenter<AudioRecorderView> {
public void onStopRecord() {
// stopped and released MediaPlayer
// ...
// some preparation and saved audio file in audioFileName variable.
getView().updateVisualizer(FileUtils.fileToBytes(new File(audioFileName)));
}
}
}
UPD: I created the library for resolving this case github.com/scrobot/SoundWaveView. It still in status "WIP"(work in progress), but soon I will complete it.
I believe Scrobot's answer does not work. It assumes the input audio to be in a certain (quite peculiar) encoding (single-channel/mono linear PCM with 5 bit depth). And the algorithm to calculate amplitudes from the wave function is probably flawed. If you use that algorithm with any commonly used audio file format, you will get nothing but random data.
The truth is: It's just a bit more complicated that that.
Here's what's there to be done to achieve the OP's goal:
Use Android's MediaExtractor to read the input audio file (variousformats/encodings are supported)
Use Android's MediaCodec to decode the input audio encoding to a linear PCM encoding with certain bit depth (usually 16 bit)
Only after this step you got a byte array which you can linearly read and calculate amplitudes from.
Apply a loudness measure to the PCM-encoded data. There are many of them, some more complicated (e.g. LUFS/LKFS), some more basic (RMS). Let's take RMS (= Root Mean Squares) for example:
Determine the number of samples per bar.
Read all the samples for a single bar. Usually there are 2 channels, so for each sample you will get 2 short ints (16 bit) for PCM-16.
For each sample calculate the mean of all channels.
Maybe you will want to normalize the value in some way, e.g. to get float values between -1 and 1 you can divide by (2 / (1 << 16))
Square each sample (hence the "S" in RMS)
Calculate the mean of all the samples for a bar (hence the "M" in RMS)
Calculate the square root of the resulting value (hence the "R" in RMS)
Now you get a value which you can base the height of the bar on.
Repeat steps 2-8 for all the bars.
To implement this is quite an involved task. I could not find any library providing this whole process already. But at least Android's media API provides the algorithms for reading audio file in any format.
Note: RMS is considered a not very accurate loudness measure. But it seems to yield results which are at least somewhat related to what you can actually hear. For many applications it should be good enough.
JETPACK COMPOSE
AudioWaveform is a lightweight Jetpack Compose library that draws a waveform of audio.
XML
WaveformSeekBar is an android library that draws a waveform from a local audio file, resource, and URL using android.view.View (XML approach).
AUDIO PROCESSING
If you're looking for a fast audio processing library, you could use the existing Amplituda library. Amplituda also has caching and compressing features out of the box.
I use followed example (described here) to animate my sprite sheet.
from example I wrote my Object:
public class Sprite {
private int x, y;
private int width, height;
private Bitmap b;
MainGamePanel ov;
int currentFrame = 0;
public Sprite(MainGamePanel mainGamePanel, Bitmap blob) {
this.ov = mainGamePanel;
this.b = blob;
// 1x12
height = b.getHeight();
width = b.getWidth()/12;
x = y = 50;
}
private void update(int dist) {
currentFrame = ++currentFrame % 12;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//return _b;
}
public void draw(int shift, Canvas canvas, int dist) {
update(dist);
int srcX = currentFrame * width;
Rect src = new Rect(srcX, 0, srcX+width, height);
Rect dst = new Rect(x, y+shift, x+width, y+height+shift);
canvas.drawBitmap(b, src, dst, null);
}
Here every 100 msec I take different part from image (Bitmap) and show it.
1 -> 2 -> 3 -> 4 -> ... -> 12
So my creature flies and moves with wings.
If I show only 1 object, it seems good but when I try to run 20 creatures in loop:
Bitmap blob = BitmapFactory.decodeResource(getResources(), R.drawable.sprite3);
for(int i=0; i<20; i++){
spriteList.add(new Sprite(this, blob));
}
....
for(int i=0; i<spriteList.size(); i++){
sprite = spriteList.get(0);
sprite.draw(canvas, dist);
}
My objects start to be slow according to drawn object count.
It happens I think because of Thread.sleep(100);.
I don't see any performance problem.
Sounds like each object sleep pauses all objects.
For 20 objects this sleep grows to 2 sec.
For sure I use workaround like:
int sleep = 100/spriteList.size();
Thread.sleep(sleep);
But code looks messy.
Can anyone help me how to fix it?
Here is sprite3 image:
[EDIT]
Maybe I need create each object in separate Thread?
You should definitely not sleep while rendering; it greatly reduces the performance, especially, as you add new Animation Clips, etc. Also, don't create objects within your onDraw method and do try to reuse the Rect objects. Creating objects during rendering is very expensive.
The below code is my attempt to send mMyView to the front or the back of the set of children of mPivotParent so it will be rendered on top or behind the others. Hiding the view will not suffice in my case.
mPivotParent is a FrameLayout.
Looking at mPivotParent.mChildren shows that my code below "works" in that the ordering is being set correctly. Yet it has no impact on the z order. Not only this, but the framerate gets cumulatively slower and slower the more times the repositioning code gets called. There are 4 children total and mPivotParent.mChildrenCount remains at 4 throughout as expected.
I'm targeting API Level 7.
#Override
public boolean onTouchEvent(MotionEvent event) {
Display display = getWindowManager().getDefaultDisplay();
float x = event.getRawX();
float sWidth = (int)display.getWidth();
float xLerpFromCenter = ((x / sWidth) - .5f) * 2.f; // [-1, 1]
mRotateAnimation.mfLerp = xLerpFromCenter;
if(xLerpFromCenter < -0.2f && mPivotParent.getChildAt(0) != mMyView)
{
mPivotParent.removeView(mMyView);
mPivotParent.addView(mMyView, 0);
refreshEverything();
}
else if(xLerpFromCenter > 0.2f && mPivotParent.getChildAt(0) == mMyView)
{
mPivotParent.removeView(mMyView);
mPivotParent.addView(mMyView, mPivotParent.getChildCount() - 1);
refreshEverything();
}
return super.onTouchEvent(event);
}
private void refreshEverything()
{
for(int i = 0; i < mPivotParent.getChildCount(); ++i)
{
mPivotParent.getChildAt(i).invalidate();
mPivotParent.getChildAt(i).requestLayout();
}
mPivotParent.invalidate();
mPivotParent.requestLayout();
}
Partial Solution
Here's a somewhat inefficient hack but it works for my purpose, which is to take the top item and send it to the back, keeping all other items in their same order.
private void putFrontAtBack()
{
for(int i = 0; i < mPivotParent.getChildCount() - 1; ++i)
{
mPivotParent.getChildAt(0).bringToFront();
}
refreshEverything();
}
Note: This doesn't work in the general case of arbitrary re-ordering.
Try this.
private void reorder(int[] order)
{
if(order == null || order.length != mPivotParent.getChildCount()) return;
for(int i = order.length - 1; i >= 0; i--)
{
mPivotParent.getChildAt(order[i]).bringToFront();
}
refreshEverything();
}
This code provides arbitrary reordering. The integer array "order" is used to indicate the new order of each view, where order[n]=x means the new order of childAt(x) is n.
I have displayed images from resource in my application as rows and columns randomly.
From those rows and columns i would like to swap the two images when user click on beside of images only.The following code will display the images in rows and columns as randomly.
private void rand(int imagesList[][])
{
Random generator = new Random();
int temp;
for (int i = 0; i < MAX_ROWS; i++)
for(int j = 0; j < MAX_COLS; j++)
{
int randRowPos = generator.nextInt(MAX_ROWS);
int randColPos = generator.nextInt(MAX_COLS);
temp = imagesList[i][j];
imagesList[i][j] = imagesList[randRowPos][randColPos];
imagesList[randRowPos][randColPos]= temp;
}
}
by using the above code i have displayed images as rows and columns.
Here how can i swap the two beside images from rows and columns?
please any body help me.....
I don't have privilege to add comment, so I am posting this as answer.
What do you mean by beside images ?
Is it when user will click on one image , it should get swapped with the image next to it ?
Can you also share the code where you have binned these images to view or any adapterview ?
EDIT :
I too had similar situation at the times when absolute layouts were alive.
What I had done is as follows:
Class:
public class PlayScreen extends Activity implements OnTouchListener
private Panel mainPanel; // Panel for out display
boolean firstClick = false;
OnCreate :
main = new Panel(this);
// Display the panel (calls the ondraw and updates the display)
setContentView(main,new ViewGroup.LayoutParams(screenwidth,screenheight));
// Listen for touchevents on the panel
main.setOnTouchListener(this);
Panel :
class Panel extends View
{
/*
* Init a Panel to draw on and a paint to paint with
*/
Paint mBitmapPaint;
public Panel(Context context)
{
super(context);
mBitmapPaint = new Paint();
}
#Override
protected void onDraw(Canvas canvas)
{
drawImages(canvas);
}
}
drawImages :
private void drawImages(Canvas canvas)
{
for(int i = 0; i<MAX_ROWS; i++){
for(int j=0; j<MAX_COLS; j++)
{
int xpos = j*bmp.getWidth()+j*2;
int ypos = i*bmp.getHeight()+i*2;
bmp = BitmapFactory.decodeResource(mContext.getResources(), items[i][j],opts);
canvas.drawBitmap(bmp,xpos,ypos,mBitmapPaint);
clickzonex.add(xpos);
clickzoney.add(ypos);
clickzonei.add(i);
clickZonej.add(j);
}
}
}
OnTouch:
onTouch(View v, MotionEvent event) :
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
// imply logic
x = (int) event.getX();
y = (int) event.getY();
for(int i = 0; i < clickzonex.size();i++)
{
if((x>clickzonex[i]) && (x<(clickzonex[i]+ bmp.getwidth())) && (y>(clickzoney[i])) && (y<(clickzoney[i]+bmp.getHeight())))
{
// we have a click in a zone so we get the card-number in that zone
if(firstClick == false)
{
itemAti=clickzonei[i];
itemAtj = clickzonej[i];
firstclick = false;
}
else
{
FirstItemToSwap = items[clickzonei[i]][clickzonej[i]];
SecondItemToSwap = items[itemAti][itemAtj];
// Now do the swaping using any algo you like.
main.postInvalidate();
firstclick = true;
}
break;
}
}
return true;
}
else
{
return false;
}
I have just tried to show you the logic using my own example and mixing it with your code. The main point is that in ondraw method just call drawcanvas and on touch just swap the items[][] and call postinvalidate method of Panel class.
I had to do something like this once. I just swapped the image references in the array and did a redraw(invalidate()) on the whole thing.
void swap(int x1, int y1, int x2, int y2) {
// swap items[x1][y1] and items[x2][y2]
........
invalidate();
}
Not quite sure what you are actually asking here, so please try to clarify the question.
One approach could be to use a ViewAnimator as a parent for each of the drawable ImageViews.
<ViewAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/slideshow_animator"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
Then once you capture the event that should trigger the swap, you can use the ViewAnimator to swap the View (in your case ImageView) it uses. You can even easily add an animation effect
http://developer.android.com/reference/android/widget/ViewAnimator.html
Hopefully this is the last step in completing my app. I need to make a bitmap within a canvas clickable that will either call a new activity that will play a video (mp4) or within the current activity play the video.
The class that displays the canvas and bitmaps is a class I use over and over to display a full image of a thumbnail. The image id is passed through an Intent. Here is the code for the full image activity (I'm very much a noob and pieced my code together one step at a time using this site and others so I apologize if it's not clean):
public class full_image extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(new BitmapView(this));
getWindow().setBackgroundDrawableResource(R.drawable.bground);
getWindow().setWindowAnimations(0);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
class BitmapView extends View {
public BitmapView(Context context) {
super(context);
}
#Override
public void onDraw(Canvas canvas) {
int imgid = getIntent().getIntExtra("Full",0);
Bitmap fullimage = BitmapFactory.decodeResource(getResources(),imgid);
Bitmap playButton = BitmapFactory.decodeResource(getResources(), R.drawable.bt_play); //Added for Video
Display display = getWindowManager().getDefaultDisplay();
int fpWidth = fullimage.getWidth();
int fpHeight = fullimage.getHeight();
int playWidth = playButton.getWidth();
int playHeight = playButton.getHeight();
int screenWidth = display.getWidth();
int screenHeight = display.getHeight();
int leftPoint = screenWidth/2 - fpWidth/2;
int topPoint = screenHeight/2 - fpHeight/2;
int leftPlayPoint = screenWidth/2 - playWidth/2;
int topPlayPoint = screenHeight/2 - playHeight/2;
canvas.drawBitmap(fullimage,leftPoint,topPoint,null);
canvas.drawBitmap(playButton,leftPlayPoint,topPlayPoint,null);
}
}
}
If possible, I would like just the playButton to house the onClickListener but if it's easier, I'm Ok with making the whole canvas clickable (if that's even possible).
I read on another question where there was a suggestion to use TouchEvent. I tried going that route but could not get it to work. Is this the right path and I just need to play around with it more to get it to work?
Thanks, J
Additional Stuff:
here's a snippet of code I found in another question. Where would I put this code into the code provided above.
public boolean onTouchEvent(MotionEvent event){
int action = event.getAction();
int x = event.getX() // or getRawX();
int y = event.getY();
switch(action){
case MotionEvent.ACTION_DOWN:
if (x >= xOfYourBitmap && x < (xOfYourBitmap + yourBitmap.getWidth())
&& y >= yOfYourBitmap && y < (yOfYourBitmap + yourBitmap.getHeight())) {
//tada, if this is true, you've started your click inside your bitmap
}
break;
}
}
I think we can't setOnClick on bitmap in canvas.
But we can use onTouch method for it. and check touch on bitmap or not using x-y position of touch.
try this code in onTouch method for get touch on bitmap...
if (x >= xOfYourBitmap && x < (xOfYourBitmap + yourBitmap.getWidth())
&& y >= yOfYourBitmap && y < (yOfYourBitmap + yourBitmap.getHeight())) {
//tada, if this is true, you've started your click inside your bitmap
}