Bugün sizinle periscope isimli canlı video paylaşım uygulamasındaki uçan kalpler ve kalp göndermenin nasıl yapıldığının kodlamasını Android java olarak paylaşacağım. 2 yıl önce yazdığım hubblescope yazılımımda da aynı şekilde bu kodları yazarak kullanmıştım. Yazılım çalıştığında ekranınızın herhangi bir yerine dokunursanız kalp çıkacaktır. Kaç kez dokunursanız o kadar kalbi ekranda göreceksiniz. Böylece bunların nasıl kodlandığını da anlamış olacaksınız. Bu kadar çok kişi aynı video yu izlerken nasıl aynı anda görüyor diye sorarsanız da kolay diyorum. XMPP bir server a socket üzerinden aynı video yayınını izleyenler aynı kanal (channel) a bağlanıp subscribe oluyor aynı anda bir de publish oluyor. Yani bir publish bağlanma bir de subscribe ama aynı anda. Sonuçta ekrana parmakla bastıkça publish event i o kanala bir json mesajı gönderiyor. Yani 1 kalp gönder gibi. Aynı anda o kanala subscribe olanlar o mesajı anında alıyor ve ekrana aynı bu yazılımda olduğu gibi basıyor. Bu kadar basit. Böylece kalp dışında mesaj da nasıl gönderiliyor ve video ları izleyenlerde anında bu mesajları nasıl görüyor onu da anlamış oluyorsunuz. Periscope daki gibi gelen mesajın bir süre sonra yavaş yavaş kaybolması nasıl oluyor o kodları da bir sonraki yazımda paylaşacağım. 🙂
Şimdi kodları manifest den başlayarak gösterelim. Kodu açıklamama gerek yok gayet net ve basit zaten .. Kalp resimlerini de en sonunda koyacağım. Bir adet de ic_launcher resmi yani android yazılımımızın iconu olacak.
AndroidManifest.xml dosyası.
<manifest xmlns:android=“http://schemas.android.com/apk/res/android” package=“com.selcukcelik.heartbubbling”
android:versionCode=“1”
android:versionName=“1.0” >
<uses-sdk
android:minSdkVersion=“21”
android:targetSdkVersion=“23” />
<application
android:icon=“@drawable/ic_launcher”
android:label=“@string/app_name”
android:theme=“@style/AppTheme” >
<activity
android:name=“.MainActivity”
android:label=“@string/title_activity_main” >
<intent-filter>
<action android:name=“android.intent.action.MAIN” />
<category android:name=“android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
</application>
</manifest>
Projede res/layout bulunması gereken
Activity_main.xml
<RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”
android:layout_width=“fill_parent”
android:layout_height=“fill_parent” >
<RelativeLayout
android:id=“@+id/lay1”
android:layout_width=“fill_parent”
android:layout_height=“700px”
android:layout_alignParentBottom=“true”
android:layout_centerInParent=“true”
android:background=“#A0C1DD” >
</RelativeLayout>
<com.selcukcelik.heartbubbling.LayoutAnimation
android:id=“@+id/content”
android:layout_width=“200dp”
android:layout_height=“200dp”
android:layout_alignParentBottom=“true”
android:layout_centerHorizontal=“true”
android:background=“#00000000” >
</com.selcukcelik.heartbubbling.LayoutAnimation>
<SurfaceView android:id=“@+id/surface_view”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:layout_gravity=“center”>
</SurfaceView>
</RelativeLayout>
res/values folder ın da ki strings.xml
<resources>
<string name=“app_name”>Flying Hearts</string>
<string name=“title_activity_main”>Flying Hearts by Selcuk Celik</string>
</resources>
Şimdi de Android Java kodlarına sıra geldi.
MainActivity.java
package com.selcukcelik.heartbubbling;
import com.selcukcelik.heartbubbling.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;
public class MainActivity extends Activity implements SurfaceHolder.Callback {
private SurfaceView surfaceView;
private LayoutAnimation mLayout;
private SurfaceHolder holder;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView = (SurfaceView) findViewById(R.id.surface_view);
surfaceView.setZOrderOnTop(true);
holder=surfaceView.getHolder();
holder.setFormat(PixelFormat.TRANSPARENT);
mLayout = (LayoutAnimation) findViewById(R.id.content);
surfaceView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
mLayout.startAnimation();
mLayout.setCount(1);
}
return true;
}
});
}
@Override
protected void onResume() {
super.onResume();
mLayout.reset();
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
}
Şimdi de son kodumuz geliyor…
LayoutAnimation.java
package com.selcukcelik.heartbubbling;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import com.selcukcelik.heartbubbling.R;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.view.animation.Animation.AnimationListener;
import android.widget.RelativeLayout;
public class LayoutAnimation extends RelativeLayout {
private static final int[] ICONS = new int[] { R.drawable.icon1,
R.drawable.icon2, R.drawable.icon3, R.drawable.icon4,
R.drawable.icon5, R.drawable.icon6 };
public static int DEFAULT_COUNT = 1;
private static final int MAX_DEGREES = 25;
private static final long DURATION_TOTAL = 1500;
private static final long DURATION_SCALE = 800;
private static final long DURATION_ALPHA = 300;
private static final long DURATION_ROTATE = 0;
private List<View> views = new ArrayList<View>();
private boolean stopanim=false;
private Context mContext;
private boolean mIsInitial;
private boolean mViewRefresh;
Random r = new Random();
private int mCount;
public void setDefaultCount(int count){
DEFAULT_COUNT=count;
}
public LayoutAnimation(Context context) {
super(context);
this.mContext = context;
initView();
}
public LayoutAnimation(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
initView();
}
public LayoutAnimation(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.mContext = context;
initView();
}
private void initView() {
mCount = DEFAULT_COUNT;
initChildrenViews();
}
private void initChildrenViews() {
removeAllViews();
views.clear();
for (int i = 0; i < mCount; i++) {
View view = new View(mContext);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
80, 80);
lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
lp.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
view.setBackgroundResource(ICONS[r.nextInt(6)]);
view.setLayoutParams(lp);
addView(view);
view.setVisibility(View.GONE);
views.add(view);
}
}
public void setCount(int count) {
this.mViewRefresh = (this.mCount != count);
this.mCount = (count < 0 ? DEFAULT_COUNT : count);
if (mViewRefresh) {
clearAnimations();
initChildrenViews();
startAnimation();
}
else
{
//clearAnimations();
initChildrenViews();
startAnimation();
}
}
private void clearAnimations() {
for (int i=0;i<views.size();i++) {
View view = views.get(i);
view.clearAnimation();
}
}
public void reset() {
this.mIsInitial = true;
}
public void stopAnim() {
this.stopanim=true;
}
public void startAnimation() {
stopanim=false;
reset();
for (int i = 0; i < views.size(); i++) {
View view = views.get(i);
view.setVisibility(View.VISIBLE);
startAnim(i, view);
}
}
private void startAnim(final int index, final View view) {
Animation anim = getAnimSet(index, view);
anim.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
removeAllViews();
views.clear();
}
});
view.startAnimation(anim);
}
private Animation getAnimSet(int index, final View view) {
float degrees;
if (index < 4 && index < views.size() – 1) {
degrees = randomDegrees((index % 2 == 0));
} else {
degrees = randomDegrees();
}
Random random = new Random();
int startOffset = 0;
if (mIsInitial) {
if (index > 0) {
startOffset = random.nextInt(300) + index * 300;
}
if (index == views.size() – 1) {
mIsInitial = false;
}
}
AnimationSet anim = new AnimationSet(false);
anim.addAnimation(getRotateAnim(degrees));
Animation sAnim = getScale();
sAnim.setStartOffset(startOffset);
anim.addAnimation(sAnim);
Animation tAnim = getTranslateAnim(degrees);
tAnim.setDuration(DURATION_TOTAL);
tAnim.setStartOffset(startOffset);
anim.addAnimation(tAnim);
Animation aAnim = getAlphaAnim();
aAnim.setStartOffset(DURATION_TOTAL – DURATION_ALPHA + startOffset);
anim.addAnimation(aAnim);
view.startAnimation(anim);
return anim;
}
private Animation getScale() {
Animation anim = new ScaleAnimation(0f, 1.5f, 0f, 1.5f,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
1.0f);
anim.setDuration(DURATION_SCALE);
anim.setFillAfter(true);
return anim;
}
private Animation getRotateAnim(final float degrees) {
Animation anim = new RotateAnimation(0f, degrees,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
1.0f);
anim.setDuration(DURATION_ROTATE);
anim.setFillAfter(true);
anim.start();
return anim;
}
private Animation getTranslateAnim(final float degrees) {
double dis = Math.tan(degrees * Math.PI / 180) * 100;
Animation anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0f,
Animation.ABSOLUTE, (float) dis, Animation.RELATIVE_TO_PARENT,
0f, Animation.ABSOLUTE, -300);
anim.start();
return anim;
}
private Animation getAlphaAnim() {
Animation anim = new AlphaAnimation(1.0f, 0.2f);
anim.setDuration(DURATION_ALPHA);
return anim;
}
private float randomDegrees() {
Random random = new Random();
return MAX_DEGREES – random.nextFloat() * MAX_DEGREES * 2;
}
private float randomDegrees(boolean isPositive) {
Random random = new Random();
if (isPositive) {
return random.nextFloat() * MAX_DEGREES;
} else {
return -random.nextFloat() * MAX_DEGREES;
}
}
}
Ve kalp resimlerine sıra geldi. Toplam 6 adet ki siz isterseniz paintbrush da bunu istediğiniz sayıda farklı renk ile aynı boyutta çoğaltabilirsiniz.. Bu resimleri de res/drawable_hdpi folder ına koyabilirsiniz.
En baştakinin adını ic_launcher.png diğerlerini de icon1.png, icon2.png,…icon6.png olarak isimlendirin.
Sağlıcakla kalın..
Selcuk Celik