Canlı kartlar zaman çizelgesinin mevcut bölümünde görünür ve o anda alakalı bilgiler görüntülemesi.
Canlı kartlar, kullanıcıların web sitenizde aktif olarak bir işle meşgul durumda ancak Google Glass'ı düzenli olarak kontrol etmek istiyorlar. sayfasına bakın. Örneğin, çalışma saatlerini kontrol etmek birkaç dakikada bir koşu sırasında bir şarkıyı atlamak veya duraklatmak istiyorlar.
Google Glass'ı ilk kez geliştiriyorsanız Devam Eden Görev rehberini okuyun tıklayın. Bu belge, eksiksiz bir planın nasıl Tasarımla ilgili en iyi uygulamalarımıza uygun olarak canlı kart yapılan cam ürünler.
İşleyiş şekli
Canlı kartlar, kartların günümüzde de var olması için bir yol sunar. zaman çizelgesinin belirli bölümlerini değiştirebilirsiniz. Statik kartlardan farklı olarak Canlı kartlar zaman çizelgesinde kalmaz ve kullanıcılar bu durumu bu dosyaları kaldırmanızı sağlar.
Kullanıcılar canlı kartları genellikle sesli komut söyleyerek başlatırlar. dokunursanız kartı oluşturan bir arka plan hizmeti başlatılır. Ardından, işlem yapabilecek menü öğelerini göstermek için karta dokunabilirler (örneğin, zaman çizelgesinden reddedebilirsiniz.)
Ne zaman kullanılır?
Canlı kartlar, kullanıcıların hemen başvurabileceği, devam eden görevler için tasarlanmıştır ve sık sık dışında (örneğin, çalışma durumunu gösteren bir ekran) bir hareket, navigasyon sırasındaki animasyonlu bir harita veya müzik çalar.
Canlı kartların bir başka avantajı da Kullanıcılarla gerçek zamanlı etkileşim gerektiren kullanıcı arayüzleri gerçek zamanlı güncellemeler yapıyoruz.
Canlı kartlar kullanılırken zaman çizelgesi kullanıcı üzerinde kontrol sahibi olmaya devam eder canlı kartta ileri veya geri kaydırma özelliğini kullanabilirsiniz. canlı karta göre işlem yapmak yerine zaman çizelgesinde gezinir. Ayrıca, ekran, sistemin davranışına göre ( kullanıcı etkileşimi olmadan veya baş gösterme sırasında 5 saniye).
Bununla birlikte canlı kartların, aynı özelliklerin çoğuna erişimi vardır. yoğunluk (ör. sensör veya GPS verileri. Bu sayede ilgi çekici deneyimler oluşturmaya devam edebilirsiniz Zaman çizelgesi deneyiminde kalarak diğer kullanıcılara kontrol edebilirsiniz.
Mimari
Canlı kartlar, yayın süresi boyunca bu kartlara sahip olunması için uzun bir bağlam gerektirir. Görünür oldukları için bunları bir arka plan hizmetinde yönetin.
Daha sonra, hizmet sunulur sunulmaz canlı bir kart yayınlayabilir ve oluşturabilirsiniz. yanıt olarak ne zaman gerçekleştiğini gösterir. Canlı kartları düşük sıklıkta (birkaç saniyede bir) oluşturabilirsiniz. veya yüksek sıklıkta (sistemin yenileme sıklığı kadar).
Canlı kart artık alakalı olmadığında hizmeti kaldırmak için oluşturmayı durdur.
Düşük Frekanslı Oluşturma
Düşük frekanslı oluşturma işlemi küçük bir Android grubuyla sınırlıdır. görüntüleme ve ekranı yalnızca bir kez güncelleyebilir. birkaç saniye.
Bu, canlı kartlar oluşturmanın basit bir yoludur ve veya sürekli oluşturma gerektirmeyen basit içerikler sık güncelleme.
Yüksek Frekanslı Oluşturma
Yüksek frekanslı oluşturma, daha fazla seçenek kullanmanıza olanak tanır Android grafik çerçevesinde kullanılabilir.
Sistem size bu zeminin gerçek arka yüzeyini verir. 2D kullanarak doğrudan üzerine çizdiğiniz canlı kart ve hatta OpenGL ile karmaşık 3D grafikler oluşturabilirsiniz.
Düşük frekanslı canlı kartlar oluşturma
Düşük frekanslı oluşturma işlemi için RemoteViews nesnesini ifade eder:
Düşük frekanslı oluşturma işlemini şu durumlarda kullanın:
- Yalnızca daha önce listelenen standart Android görünümü API'lerini kullanmanız gerekir.
- Yalnızca nispeten seyrek aralıklarla güncelleme yapmanız gerekir (aralarında birkaç saniye yenilemesi).
Unutmayın:
- Canlı kartların her zaman
PendingIntent
şununla açıklandı:setAction()
. - Yayınladıktan sonra bir kartta değişiklik yapmak için şu çağrıyı yapın:
setViews()
Güncellenmiş RemoteViews öğeleri içeren kartta nesnesini tanımlayın.
Düşük frekanslı canlı kartlar oluşturmak için:
Oluşturmak istediğiniz düzeni veya görünümü oluşturun. Aşağıdaki örnek hayali bir basketbol maçına ait düzeni gösteriyor:
<TextView android:id="@+id/home_team_name_text_view" android:layout_width="249px" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:gravity="center" android:textSize="40px" /> <TextView android:id="@+id/away_team_name_text_view" android:layout_width="249px" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:gravity="center" android:textSize="40px" /> <TextView android:id="@+id/away_score_text_view" android:layout_width="249px" android:layout_height="wrap_content" android:layout_alignLeft="@+id/away_team_name_text_view" android:layout_below="@+id/away_team_name_text_view" android:gravity="center" android:textSize="70px" /> <TextView android:id="@+id/home_score_text_view" android:layout_width="249px" android:layout_height="wrap_content" android:layout_alignLeft="@+id/home_team_name_text_view" android:layout_below="@+id/home_team_name_text_view" android:gravity="center" android:textSize="70px" /> <TextView android:id="@+id/footer_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:layout_marginBottom="33px" android:textSize="26px" />
Yayındaki kartı yöneten ve düzeninizi veya görünümünüzü oluşturan bir hizmet oluşturun. Bu örnek hizmet, hayali bir basketbol maçının skorunu her zaman günceller. 30 saniye.
import java.util.Random; import com.google.android.glass.timeline.LiveCard; import com.google.android.glass.timeline.LiveCard.PublishMode; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.widget.RemoteViews; public class LiveCardService extends Service { private static final String LIVE_CARD_TAG = "LiveCardDemo"; private LiveCard mLiveCard; private RemoteViews mLiveCardView; private int homeScore, awayScore; private Random mPointsGenerator; private final Handler mHandler = new Handler(); private final UpdateLiveCardRunnable mUpdateLiveCardRunnable = new UpdateLiveCardRunnable(); private static final long DELAY_MILLIS = 30000; @Override public void onCreate() { super.onCreate(); mPointsGenerator = new Random(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (mLiveCard == null) { // Get an instance of a live card mLiveCard = new LiveCard(this, LIVE_CARD_TAG); // Inflate a layout into a remote view mLiveCardView = new RemoteViews(getPackageName(), R.layout.main_layout); // Set up initial RemoteViews values homeScore = 0; awayScore = 0; mLiveCardView.setTextViewText(R.id.home_team_name_text_view, getString(R.string.home_team)); mLiveCardView.setTextViewText(R.id.away_team_name_text_view, getString(R.string.away_team)); mLiveCardView.setTextViewText(R.id.footer_text, getString(R.string.game_quarter)); // Set up the live card's action with a pending intent // to show a menu when tapped Intent menuIntent = new Intent(this, MenuActivity.class); menuIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); mLiveCard.setAction(PendingIntent.getActivity( this, 0, menuIntent, 0)); // Publish the live card mLiveCard.publish(PublishMode.REVEAL); // Queue the update text runnable mHandler.post(mUpdateLiveCardRunnable); } return START_STICKY; } @Override public void onDestroy() { if (mLiveCard != null && mLiveCard.isPublished()) { //Stop the handler from queuing more Runnable jobs mUpdateLiveCardRunnable.setStop(true); mLiveCard.unpublish(); mLiveCard = null; } super.onDestroy(); } /** * Runnable that updates live card contents */ private class UpdateLiveCardRunnable implements Runnable{ private boolean mIsStopped = false; /* * Updates the card with a fake score every 30 seconds as a demonstration. * You also probably want to display something useful in your live card. * * If you are executing a long running task to get data to update a * live card(e.g, making a web call), do this in another thread or * AsyncTask. */ public void run(){ if(!isStopped()){ // Generate fake points. homeScore += mPointsGenerator.nextInt(3); awayScore += mPointsGenerator.nextInt(3); // Update the remote view with the new scores. mLiveCardView.setTextViewText(R.id.home_score_text_view, String.valueOf(homeScore)); mLiveCardView.setTextViewText(R.id.away_score_text_view, String.valueOf(awayScore)); // Always call setViews() to update the live card's RemoteViews. mLiveCard.setViews(mLiveCardView); // Queue another score update in 30 seconds. mHandler.postDelayed(mUpdateLiveCardRunnable, DELAY_MILLIS); } } public boolean isStopped() { return mIsStopped; } public void setStop(boolean isStopped) { this.mIsStopped = isStopped; } } @Override public IBinder onBind(Intent intent) { /* * If you need to set up interprocess communication * (activity to a service, for instance), return a binder object * so that the client can receive and modify data in this service. * * A typical use is to give a menu activity access to a binder object * if it is trying to change a setting that is managed by the live card * service. The menu activity in this sample does not require any * of these capabilities, so this just returns null. */ return null; } }
Yüksek frekanslı canlı kartlar oluşturma
Yüksek frekanslı oluşturma, doğrudan çizim yapmanızı sağlar arka Yüzeyde canlı kartın.
Yüksek frekanslı oluşturma işlemini şu durumlarda kullanın:
- Yayındaki kartı sık sık (saniyede birkaç kez) güncellemeniz gerekiyor.
- Oluşturabileceğiniz öğeler konusunda esnekliğe ihtiyacınız var. Yüksek frekanslı oluşturma, karmaşık OpenGL grafikleri için Android görünümlerini ve düzenlerini kullanır.
Unutmayın:
- Yayındaki kartın yüzeyinde oluşturmak için her zaman bir arka plan hizmeti oluşturmanız gerekir.
- Canlı kartların her zaman
PendingIntent
şununla açıklandı:setAction()
. - Şu durumda
GLRenderer
alanını kullanın: OpenGL veDirectRenderingCallback
kullanabilirsiniz.
DirectRenderingCallback Kullanma
Standart Android görünümleri ve çizim mantığıyla canlı kartlar oluşturmak için:
Şunları uygulayan bir sınıf oluşturun:
DirectRenderingCallback
Bu arayüzlere geri çağırma özellikleri, canlı kartın yüzey yaşam döngüsündeki önemli olaylar sırasında işlem yapmanızı sağlar.Aşağıdaki örnek düzenli olarak oluşturulacak bir arka plan ileti dizisi oluşturur ancak harici etkinliklere yanıt olarak kartı güncelleyebilir (örneğin, sensör veya konum güncellemeleri).
public class LiveCardRenderer implements DirectRenderingCallback { // About 30 FPS. private static final long FRAME_TIME_MILLIS = 33; private SurfaceHolder mHolder; private boolean mPaused; private RenderThread mRenderThread; @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // Update your views accordingly. } @Override public void surfaceCreated(SurfaceHolder holder) { mPaused = false; mHolder = holder; updateRendering(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { mHolder = null; updateRendering(); } @Override public void renderingPaused(SurfaceHolder holder, boolean paused) { mPaused = paused; updateRendering(); } /** * Start or stop rendering according to the timeline state. */ private void updateRendering() { boolean shouldRender = (mHolder != null) && !mPaused; boolean rendering = mRenderThread != null; if (shouldRender != rendering) { if (shouldRender) { mRenderThread = new RenderThread(); mRenderThread.start(); } else { mRenderThread.quit(); mRenderThread = null; } } } /** * Draws the view in the SurfaceHolder's canvas. */ private void draw() { Canvas canvas; try { canvas = mHolder.lockCanvas(); } catch (Exception e) { return; } if (canvas != null) { // Draw on the canvas. mHolder.unlockCanvasAndPost(canvas); } } /** * Redraws in the background. */ private class RenderThread extends Thread { private boolean mShouldRun; /** * Initializes the background rendering thread. */ public RenderThread() { mShouldRun = true; } /** * Returns true if the rendering thread should continue to run. * * @return true if the rendering thread should continue to run */ private synchronized boolean shouldRun() { return mShouldRun; } /** * Requests that the rendering thread exit at the next opportunity. */ public synchronized void quit() { mShouldRun = false; } @Override public void run() { while (shouldRun()) { draw(); SystemClock.sleep(FRAME_TIME_MILLIS); } } } }
DirectRenderingCallback
öğenizin bir örneğini ayarlayınLiveCard
olarakSurfaceHolder
geri araması. Bu canlı karta kendini oluşturmak için hangi mantığın kullanılacağını bildirir.// Tag used to identify the LiveCard in debugging logs. private static final String LIVE_CARD_TAG = "my_card"; // Cached instance of the LiveCard created by the publishCard() method. private LiveCard mLiveCard; private void publishCard(Context context) { if (mLiveCard == null) { mLiveCard = new LiveCard(this, LIVE_CARD_TAG); // Enable direct rendering. mLiveCard.setDirectRenderingEnabled(true); mLiveCard.getSurfaceHolder().addCallback( new LiveCardRenderer()); Intent intent = new Intent(context, MenuActivity.class); mLiveCard.setAction(PendingIntent.getActivity(context, 0, intent, 0)); mLiveCard.publish(LiveCard.PublishMode.SILENT); } else { // Card is already published. return; } } private void unpublishCard(Context context) { if (mLiveCard != null) { mLiveCard.unpublish(); mLiveCard = null; } }
OpenGL kullanma
Şunları uygulayan bir sınıf oluşturun:
GlRenderer
. Geri çağırmaları bu arayüzde uygulamak, önemli etkinlikler sırasında işlem yapmanızı sağlar canlı kartın yüzey yaşam döngüsüne ilişkindir. Bu örnekte renkli, dönen bir küp çiziliyor.import com.google.android.glass.timeline.GlRenderer; import android.opengl.GLES20; import android.opengl.Matrix; import android.os.SystemClock; import java.util.concurrent.TimeUnit; import javax.microedition.khronos.egl.EGLConfig; /** * Renders a 3D OpenGL Cube on a {@link LiveCard}. */ public class CubeRenderer implements GlRenderer { /** Rotation increment per frame. */ private static final float CUBE_ROTATION_INCREMENT = 0.6f; /** The refresh rate, in frames per second. */ private static final int REFRESH_RATE_FPS = 60; /** The duration, in milliseconds, of one frame. */ private static final float FRAME_TIME_MILLIS = TimeUnit.SECONDS.toMillis(1) / REFRESH_RATE_FPS; private final float[] mMVPMatrix; private final float[] mProjectionMatrix; private final float[] mViewMatrix; private final float[] mRotationMatrix; private final float[] mFinalMVPMatrix; private Cube mCube; private float mCubeRotation; private long mLastUpdateMillis; public CubeRenderer() { mMVPMatrix = new float[16]; mProjectionMatrix = new float[16]; mViewMatrix = new float[16]; mRotationMatrix = new float[16]; mFinalMVPMatrix = new float[16]; // Set the fixed camera position (View matrix). Matrix.setLookAtM(mViewMatrix, 0, 0.0f, 0.0f, -4.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); } @Override public void onSurfaceCreated(EGLConfig config) { // Set the background frame color GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); GLES20.glClearDepthf(1.0f); GLES20.glEnable(GLES20.GL_DEPTH_TEST); GLES20.glDepthFunc(GLES20.GL_LEQUAL); mCube = new Cube(); } @Override public void onSurfaceChanged(int width, int height) { float ratio = (float) width / height; GLES20.glViewport(0, 0, width, height); // This projection matrix is applied to object coordinates in the onDrawFrame() method. Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1.0f, 1.0f, 3.0f, 7.0f); // modelView = projection x view Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0); } @Override public void onDrawFrame() { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); // Apply the rotation. Matrix.setRotateM(mRotationMatrix, 0, mCubeRotation, 1.0f, 1.0f, 1.0f); // Combine the rotation matrix with the projection and camera view Matrix.multiplyMM(mFinalMVPMatrix, 0, mMVPMatrix, 0, mRotationMatrix, 0); // Draw cube. mCube.draw(mFinalMVPMatrix); updateCubeRotation(); } /** Updates the cube rotation. */ private void updateCubeRotation() { if (mLastUpdateMillis != 0) { float factor = (SystemClock.elapsedRealtime() - mLastUpdateMillis) / FRAME_TIME_MILLIS; mCubeRotation += CUBE_ROTATION_INCREMENT * factor; } mLastUpdateMillis = SystemClock.elapsedRealtime(); } }
Yayındaki kartı yöneten ve canlı kartın oluşturucusu olarak
CubeRenderer
sınıfını kullanır.import com.google.android.glass.timeline.LiveCard; import com.google.android.glass.timeline.LiveCard.PublishMode; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.os.IBinder; /** * Creates a {@link LiveCard} rendering a rotating 3D cube with OpenGL. */ public class OpenGlService extends Service { private static final String LIVE_CARD_TAG = "opengl"; private LiveCard mLiveCard; @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (mLiveCard == null) { mLiveCard = new LiveCard(this, LIVE_CARD_TAG); mLiveCard.setRenderer(new CubeRenderer()); mLiveCard.setAction( PendingIntent.getActivity(this, 0, new Intent(this, MenuActivity.class), 0)); mLiveCard.publish(PublishMode.REVEAL); } else { mLiveCard.navigate(); } return START_STICKY; } @Override public void onDestroy() { if (mLiveCard != null && mLiveCard.isPublished()) { mLiveCard.unpublish(); mLiveCard = null; } super.onDestroy(); } }
import android.opengl.GLES20;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
/**
* Renders a 3D Cube using OpenGL ES 2.0.
*
* For more information on how to use OpenGL ES 2.0 on Android, see the
* <a href="//developer.android.com/training/graphics/opengl/index.html">
* Displaying Graphics with OpenGL ES</a> developer guide.
*/
public class Cube {
/** Cube vertices */
private static final float VERTICES[] = {
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f
};
/** Vertex colors. */
private static final float COLORS[] = {
0.0f, 1.0f, 1.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f,
};
/** Order to draw vertices as triangles. */
private static final byte INDICES[] = {
0, 1, 3, 3, 1, 2, // Front face.
0, 1, 4, 4, 5, 1, // Bottom face.
1, 2, 5, 5, 6, 2, // Right face.
2, 3, 6, 6, 7, 3, // Top face.
3, 7, 4, 4, 3, 0, // Left face.
4, 5, 7, 7, 6, 5, // Rear face.
};
/** Number of coordinates per vertex in {@link VERTICES}. */
private static final int COORDS_PER_VERTEX = 3;
/** Number of values per colors in {@link COLORS}. */
private static final int VALUES_PER_COLOR = 4;
/** Vertex size in bytes. */
private final int VERTEX_STRIDE = COORDS_PER_VERTEX * 4;
/** Color size in bytes. */
private final int COLOR_STRIDE = VALUES_PER_COLOR * 4;
/** Shader code for the vertex. */
private static final String VERTEX_SHADER_CODE =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"attribute vec4 vColor;" +
"varying vec4 _vColor;" +
"void main() {" +
" _vColor = vColor;" +
" gl_Position = uMVPMatrix * vPosition;" +
"}";
/** Shader code for the fragment. */
private static final String FRAGMENT_SHADER_CODE =
"precision mediump float;" +
"varying vec4 _vColor;" +
"void main() {" +
" gl_FragColor = _vColor;" +
"}";
private final FloatBuffer mVertexBuffer;
private final FloatBuffer mColorBuffer;
private final ByteBuffer mIndexBuffer;
private final int mProgram;
private final int mPositionHandle;
private final int mColorHandle;
private final int mMVPMatrixHandle;
public Cube() {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(VERTICES.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
mVertexBuffer = byteBuffer.asFloatBuffer();
mVertexBuffer.put(VERTICES);
mVertexBuffer.position(0);
byteBuffer = ByteBuffer.allocateDirect(COLORS.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
mColorBuffer = byteBuffer.asFloatBuffer();
mColorBuffer.put(COLORS);
mColorBuffer.position(0);
mIndexBuffer = ByteBuffer.allocateDirect(INDICES.length);
mIndexBuffer.put(INDICES);
mIndexBuffer.position(0);
mProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(mProgram, loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_CODE));
GLES20.glAttachShader(
mProgram, loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_CODE));
GLES20.glLinkProgram(mProgram);
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
mColorHandle = GLES20.glGetAttribLocation(mProgram, "vColor");
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
}
/**
* Encapsulates the OpenGL ES instructions for drawing this shape.
*
* @param mvpMatrix The Model View Project matrix in which to draw this shape
*/
public void draw(float[] mvpMatrix) {
// Add program to OpenGL environment.
GLES20.glUseProgram(mProgram);
// Prepare the cube coordinate data.
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glVertexAttribPointer(
mPositionHandle, 3, GLES20.GL_FLOAT, false, VERTEX_STRIDE, mVertexBuffer);
// Prepare the cube color data.
GLES20.glEnableVertexAttribArray(mColorHandle);
GLES20.glVertexAttribPointer(
mColorHandle, 4, GLES20.GL_FLOAT, false, COLOR_STRIDE, mColorBuffer);
// Apply the projection and view transformation.
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
// Draw the cube.
GLES20.glDrawElements(
GLES20.GL_TRIANGLES, INDICES.length, GLES20.GL_UNSIGNED_BYTE, mIndexBuffer);
// Disable vertex arrays.
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mColorHandle);
}
/** Loads the provided shader in the program. */
private static int loadShader(int type, String shaderCode){
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}
Canlı karta odaklanma
Canlı bir kartı LiveCard.publish()
ile yayınladığınızda buna bir parametre iletirsiniz.
hemen odaklanıp odaklanmayacağını kontrol etmek için.
Zaman çizelgesinin yayınlandıktan hemen sonra karta atlanmasını sağlamak için
LiveCard.PublishMode.REVEAL
.
Kartı sessizce yayınlamak ve kullanıcıların karta kendi başlarına gitmelerini sağlamak için
LiveCard.PublishMode.SILENT
.
Ayrıca, LiveCard.navigate()
yöntemi, yayınlandıktan sonra karta geçmenizi sağlar. Örneğin, kullanıcılar
Canlı Kart'ı ana ses menüsünden çıkarırsanız canlı yayına atlayabilirsiniz.
kartı kullanabilirsiniz.
Menü oluşturma ve görüntüleme
Canlı kartlar kendi menü sistemlerini gösteremediğinden menü görüntülemek için etkinlik oluşturmanız gerekir canlı kart için geçerli olur.
Böylece menü etkinliği, canlı kartı durduracak öğeler içerebilir. yoğunlaşmayı veya gerçekleştirmek istediğiniz başka bir eylemde bulunmayı unutmayın. Ayrıca şunları da ekleyebilirsiniz: ses denetimi gibi sistem ayarları etkinliklerini menü öğesi olarak kullanabilirsiniz. Daha fazla bkz. Başlangıç ayarları.
Menü kaynakları oluşturma
Menü kaynakları oluşturma işlemi Android platformuyla aynıdır, ancak şu talimatları uygulayın: yönergeler:
- Her menü öğesi için 50 × 50 piksel boyutunda bir menü öğesi simgesi sağlayın. Menü simge, şeffaf bir arka plan üzerinde beyaz renkli olmalıdır. Bkz. Şunun için cam menü öğesi simgeleri: kullanabilirsiniz.
- İşlemi açıklayan ve yalnızca ilk harfler büyük olacak şekilde kısa bir ad kullanın. Zorunlu fiil, işe yarar iyi (örneğin, Paylaş veya Tümünü yanıtla).
- Glass, menü öğesi olmayan canlı kartları göstermiyor. En azından Kullanıcıların yayındaki kartı ekrandan kaldırabilmesi için bir Durdur menü öğesi sağlayın. önceliklendirebilirsiniz.
İlgili içeriği oluşturmak için kullanılan CheckBox widget'ı desteklenmez.
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/menu_item_1" android:title="@string/Menu_Item_1" <!-- must have "Stop" menu item --> android:icon="@drawable/menu_item_1_icon" /> <!-- white on transparent icon --> </menu>
Menü geri çağırmalarını işlemek için etkinlik oluşturma
Kullanıcılar karta dokunduğunda canlı kartınızın çağrıştıracağı bir menü etkinliği tanımlamanız gerekir.
Aşağıdakileri geçersiz kıl:
Activity
geri çağırma yöntemleri
menü etkinliğinizde menüleri düzgün bir şekilde oluşturmak, göstermek ve kapatmak için:
onCreateOptionsMenu()
XML menü kaynağını şişirir.onAttachedToWindow()
menü, aktiviteye odaklanıldığında gösterilir.onPrepareOptionsMenu()
gerekirse menü öğelerini gösterir veya gizler. Örneğin, farklı menü öğeleri gösterebilirsiniz. bir ürün veya hizmet sunabilir. Örneğin, menüdeki menü tabletine göre bazı bağlamsal veriler kullanın.onOptionsItemSelected()
, kullanıcı seçimini işler.onOptionsMenuClosed()
simgesini tıklayın.
Menü bir seçimle veya hızlıca aşağı kaydırılarak kapatıldığında etkinliğin düzgün bir şekilde tamamlanması için burada bitirmeniz gerekir.
/**
* Activity showing the options menu.
*/
public class MenuActivity extends Activity {
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
openOptionsMenu();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.stopwatch, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection.
switch (item.getItemId()) {
case R.id.stop:
stopService(new Intent(this, StopwatchService.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onOptionsMenuClosed(Menu menu) {
// Nothing else to do, closing the activity.
finish();
}
}
Menü etkinliğini şeffaf hale getirmek
Glass stiliyle tutarlı olmak için menü etkinliğini yarı saydamdır. Böylece, canlı kart aşağıda görünmeye devam eder. tıklayın:
res/values/styles.xml
dosyası oluşturun ve bir stil bildirin arka planını şeffaf hale getiren bir etiket içerir:<resources> <style name="MenuTheme" parent="@android:style/Theme.DeviceDefault"> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:colorBackgroundCacheHint">@null</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowAnimationStyle">@null</item> </style> </resources>
AndroidManifest.xml
dosyanızda, temayı menü etkinliğine atayın:<?xml version="1.0" encoding="utf-8"?> <manifest ... > ... <application ... > ... <activity android:name=".MenuActivity" android:theme="@style/MenuTheme" ...> </activity> </application> </manifest>
Menüyü gösterme
Lütfen
PendingIntent
setAction()
kullanılarak kart işlemine ilişkin açıklama. Beklemedeki intent, başlamak için kullanılır
kullanıcılar karta dokunduğunda menü etkinliği:
Intent menuIntent = new Intent(this, MenuActivity.class);
mLiveCard.setAction(PendingIntent.getActivity(this, 0, menuIntent, 0));
mLiveCard.publish(LiveCard.PublishMode.REVEAL); // or SILENT
Bağlamsal sesli komutları destekleme
MenuActivity
tarafından desteklendiğinizi belirtin bağlamsal sesli komutlar:// Initialize your LiveCard as usual. mLiveCard.setVoiceActionEnabled(true); mLiveCard.publish(LiveCard.PublishMode.REVEAL); // or SILENT
MenuActivity
öğenizi, ses akışı üzerinden çağrıyı destekleyecek şekilde değiştirin:/** * Activity showing the options menu. */ public class MenuActivity extends Activity { private boolean mFromLiveCardVoice; private boolean mIsFinishing; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mFromLiveCardVoice = getIntent().getBooleanExtra(LiveCard.EXTRA_FROM_LIVECARD_VOICE, false); if (mFromLiveCardVoice) { // When activated by voice from a live card, enable voice commands. The menu // will automatically "jump" ahead to the items (skipping the guard phrase // that was already said at the live card). getWindow().requestFeature(WindowUtils.FEATURE_VOICE_COMMANDS); } } @Override public void onAttachedToWindow() { super.onAttachedToWindow(); if (!mFromLiveCardVoice) { openOptionsMenu(); } } @Override public boolean onCreatePanelMenu(int featureId, Menu menu) { if (isMyMenu(featureId)) { getMenuInflater().inflate(R.menu.stopwatch, menu); return true; } return super.onCreatePanelMenu(featureId, menu); } @Override public boolean onPreparePanel(int featureId, View view, Menu menu) { if (isMyMenu(featureId)) { // Don't reopen menu once we are finishing. This is necessary // since voice menus reopen themselves while in focus. return !mIsFinishing; } return super.onPreparePanel(featureId, view, menu); } @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { if (isMyMenu(featureId)) { // Handle item selection. switch (item.getItemId()) { case R.id.stop_this: stopService(new Intent(this, StopwatchService.class)); return true; } } return super.onMenuItemSelected(featureId, item); } @Override public void onPanelClosed(int featureId, Menu menu) { super.onPanelClosed(featureId, menu); if (isMyMenu(featureId)) { // When the menu panel closes, either an item is selected from the menu or the // menu is dismissed by swiping down. Either way, we end the activity. isFinishing = true; finish(); } } /** * Returns {@code true} when the {@code featureId} belongs to the options menu or voice * menu that are controlled by this menu activity. */ private boolean isMyMenu(int featureId) { return featureId == Window.FEATURE_OPTIONS_PANEL || featureId == WindowUtils.FEATURE_VOICE_COMMANDS; } }
Şu bölüme göz atın: bağlama dayalı sesli komutlar rehberini inceleyin.
Menü yardımcı programları
Menülerin görünümünü ve davranışını değiştirmek için birkaç yardımcı yöntem vardır. Görüntüleyin
MenuUtils
konulu videomuzu izleyin.