顯示具有 eclipse 標籤的文章。 顯示所有文章
顯示具有 eclipse 標籤的文章。 顯示所有文章

2014年12月29日 星期一

[Android] No resource found that matches the given name '@style/Theme.AppCompat.Light' / 解決方法

剛開始安裝android / eclipse的時候通常都會遇到這個問題,

相信這個問題困擾許多人已久(包括我)...終於了解+找到解法以後把他寫起來以免下次又忘記!


首先,甚麼叫做IsLibrary?

這個選項打勾就代表說你把該專案當成library(所以不能亂勾,雖然勾了也沒太大影響(?))


Step1:
加入library(File->Import)



選擇sdk-extras-android-support-v7-appcompat資料夾,記得要勾選copy to workspace

Step2:

確認專案->右鍵Properties->anddroid(左側)->Library(isLibrary)是否勾選


Step3:

進到目標Project(就是有錯誤的那個project)

同樣也是properties->android->library->把錯誤的remove掉



add->剛剛新建立的android-support-v7-appcompat->OK (這邊請不要勾選is library)


Step4:

又發現錯誤!!



在builders這邊也有錯所以我們在Builders->Libraries->選擇錯誤的項目->edit->選圖片內的路徑

下面這個也有錯,所以也修正過來

Step5:

完成! 看到style.xml這邊完全沒有錯誤了,project也是沒有驚嘆號出現


就是這樣!幾個步驟做完就可以囉!

如果還是無法記得要去SDK Manager下載新的extras(要按install)

END


2014年9月5日 星期五

[Android] 儀表板式顯示數值,類Gauge View / 使用Seek Circle 實作



This is a Tutorial about android - like Gauge View or Seek Circle, edit by civetcat.

if you need to do something like meter on UI / Speed meter or anything like that ,

you can just download the zip file,watch the code.

Download Link: SeekCircle-Thread-version  // SeekCircle-Normal-version

Original Code : https://github.com/Necat0r/SeekCircle
------------------------------------------------------------------------------------------------------------

以上為給國外朋友參考用。

正文開始:

  因為有需要,所以研究了一下如何在Android上面做出一個儀表板型的UI,一開始看了很多國

內的文章幾乎都沒有寫,只有大陸那邊網站稍稍有提到一些,但下載回來都是亂碼,好不容易找到

了一個國外的open-source,於是就下載回來自己修改。

  過程當中刪除了一些我覺得不是很必要的code,如果要的話也是可以去原網址這裡下載來

看看,使用方法為用手觸控來調整百分比(要滑動那個圓),看起來滿炫的!但我需要的不是用觸控

型的,所以這邊修改了一下改為你餵數值進去才會動。

第一個版本跟第二個版本其實沒有差很多,只是一個把餵數值的code另外獨立出來做一個thread

而已。


Code這邊可以分為三部分: MainActivity / SeekCircle / ProgressCircle


MainActivity當然就是主程式

/**
 * MainActivity.java [V 1.0.0]
 * classes :﹛com.caryfish.circleprogress.MainActivity
 * 剢豌  create at 2014-8-18 狟敁4:20:40
 */
package com.civetcat.circleprogress;

import com.caryfish.circleprogress.R;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {

public SeekCircle s_circle;
public static Handler handler;
public sending_thread send_thread;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

SeekCircle seekCircle = (SeekCircle) findViewById(R.id.seekCircle);// 建一個seekCircle物件,找id
Log.d("MainActivity", "start...");

try {
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
int[] receive_obj = (int[]) msg.obj; // object無法傳單一int
// 所以用int[]來傳送
updateText(receive_obj[0]);
break;
}
}
};
} catch (Exception e) {
Log.d("Handler exception", e.toString());
}

send_thread = new sending_thread();
send_thread.start();
// **這邊改用Thread.start() 讓他跑,updateText()則用handler去接收來更新資料

}

private void updateText(int input_progress) {
SeekCircle seekCircle = (SeekCircle) findViewById(R.id.seekCircle);
TextView textProgress = (TextView) findViewById(R.id.textProgress);

if (textProgress != null && seekCircle != null) {

seekCircle.setProgress(input_progress);
int progress = seekCircle.getProgress();// 會去ProgressCircle找getProgress()拿資料
textProgress.setText(Integer.toString(progress) + "%");
}
}
}

這邊因為要接收從另個thread來的數據,所以使用handler(請不要使用setText之類的,保證會有錯誤),updateText的部分則是會跑去setProgress(ProgressCircle.java)然後再把set好的數值用getProgress(同樣為ProgressCircle.java)取出來放在text裡面。

這部分應該是最簡單的部分,接下來兩個就是關鍵核心了!

雖然SeekCircle.java看似無用,但也是要他的建構子,所以不能把該檔案刪掉。

package com.civetcat.circleprogress;

import android.content.Context;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;

public class SeekCircle extends ProgressCircle
{
/**
* A callback that notifies clients when the progress level has been
* changed. This includes changes that were initiated by the user through a
* touch gesture or arrow key/trackball as well as changes that were
* initiated programmatically.
*/

//SeekCircle建構子,勿刪
public SeekCircle(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
Log.d("SeekCircle","in Seek Circle constructor");
}

public SeekCircle(Context context, AttributeSet attrs)
{
super(context, attrs);
Log.d("SeekCircle","in Seek Circle constructor");
}

public SeekCircle(Context context)
{
super(context);
Log.d("SeekCircle","in Seek Circle constructor");
}
//SeekCircle建構子,勿刪 end
}


這邊能夠調整整個圖形介面的就屬ProgressCircle.java了(MainActivity也是拉...)

package com.civetcat.circleprogress;

import com.caryfish.circleprogress.R;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

//import com.seekcircle.library.R;

public class ProgressCircle extends View
{
private float mRingBias = 0.15f;
private float mSectionRatio = 5.0f;
private RectF mSectionRect = new RectF();
protected float mSectionHeight;

protected float mRadius;

protected int mMaxProgress;
protected int mProgress;

protected float mCenterX;
protected float mCenterY;

private Paint mPaint;
private int mColor1;
private int mColor2;
private int mInactiveColor;

{
mMaxProgress = 100;
mProgress = 0;

mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);

mColor1 = Color.parseColor("#ffff5900"); //頭端color = 紅
mColor2 = Color.parseColor("#ff33b5e5"); //尾端color = 藍
mInactiveColor = Color.parseColor("#ff404040");

mPaint.setColor(mColor1); // Set default
}
//------建構子
public ProgressCircle(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);

initAttributes(context, attrs);
}

public ProgressCircle(Context context, AttributeSet attrs)
{
super(context, attrs);

initAttributes(context, attrs);
}

public ProgressCircle(Context context)
{
super(context);
}

//建構子----end

private void initAttributes(Context context, AttributeSet attrs)
{
TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SeekCircle, 0, 0);
try
{
// Read and clamp max
int max = attributes.getInteger(R.styleable.SeekCircle_max, 100);
mMaxProgress = Math.max(max, 1);

// Read and clamp progress
int progress = attributes.getInteger(R.styleable.SeekCircle_progress, 0);
mProgress = Math.max(Math.min(progress, mMaxProgress), 0);
}
finally
{
attributes.recycle();
}
}

private void updateDimensions(int width, int height)
{
// Update center position
mCenterX = width / 2.0f;
mCenterY = height / 2.0f;

// Find shortest dimension
int diameter = Math.min(width, height);

float outerRadius = diameter / 2;
float sectionHeight = outerRadius * mRingBias;
float sectionWidth = sectionHeight / mSectionRatio;

mRadius = outerRadius - sectionHeight / 2;
mSectionRect.set(-sectionWidth / 2, -sectionHeight / 2, sectionWidth / 2, sectionHeight / 2);
mSectionHeight = sectionHeight;
}

@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);

if (width > height)
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
else
super.onMeasure(widthMeasureSpec, widthMeasureSpec);

updateDimensions(getWidth(), getHeight());
}

@Override //改變size大小
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);

updateDimensions(w, h);
}

@Override
protected void onDraw(Canvas canvas)
{
// Center our canvas
canvas.translate(mCenterX, mCenterY);//偏移原本原點多少x,多少y ex:100,100 -> 101,101

float rotation = 360.0f / (float) mMaxProgress; //rotation 旋轉 360/100
for (int i = 0; i < mMaxProgress; ++i)
{
canvas.save();

canvas.rotate((float) i * rotation);
canvas.translate(0, -mRadius);

if (i < mProgress)
{
float bias = (float) i / (float) (mMaxProgress - 1);
int color = interpolateColor(mColor1, mColor2, bias);//漸層顏色
mPaint.setColor(color);//塗上顏色,在這一段
}
else
{
canvas.scale(0.7f, 0.7f);
mPaint.setColor(mInactiveColor);
}

canvas.drawRect(mSectionRect, mPaint);
canvas.restore();
}

super.onDraw(canvas);
}

private float interpolate(float a, float b, float bias) //interpolate = 插值,bias = 偏移,偏差
{
return (a + ((b - a) * bias));
}

private int interpolateColor(int colorA, int colorB, float bias)
{
float[] hsvColorA = new float[3];
Color.colorToHSV(colorA, hsvColorA);

float[] hsvColorB = new float[3];
Color.colorToHSV(colorB, hsvColorB);

hsvColorB[0] = interpolate(hsvColorA[0], hsvColorB[0], bias);
hsvColorB[1] = interpolate(hsvColorA[1], hsvColorB[1], bias);
hsvColorB[2] = interpolate(hsvColorA[2], hsvColorB[2], bias);

// NOTE For some reason the method HSVToColor fail in edit mode. Just use the start color for now
if (isInEditMode())
return colorA;

return Color.HSVToColor(hsvColorB);
}

/**
* Get max progress
*
* @return Max progress
*/
public float getMax()
{
return mMaxProgress;
}

/**
* Set max progress
*
* @param max
*/
public void setMax(int max)
{
int newMax = Math.max(max, 1);
if (newMax != mMaxProgress)
mMaxProgress = newMax;

updateProgress(mProgress);
invalidate();//重新繪製View
}

/**
* Get Progress
*
* @return progress
*/
public int getProgress()
{
Log.d("ProgressCircle","getProgress");
Log.d("ProgressCircle on getProgress,mProgress = ",Integer.toString(mProgress));
return mProgress;
}

/**
* Set progress
*
* @param progress
*/
public void setProgress(int progress)
{
Log.d("ProgressCircle","setProgress");
updateProgress(progress);//設定progress
}

/**
* Update progress internally. Clamp it to a valid range and invalidate the view if necessary
*
* @param progress
* @return true if progress was changed and the view needs an update
*/
protected boolean updateProgress(int progress)
{
Log.d("ProgressCircle","update Progress");
Log.d("ProgressCircle Update Progress,progress = ",Integer.toString(progress));
Log.d("ProgressCircle Update Progress,mProgress = ",Integer.toString(mProgress));
// Clamp progress , clamp = 抓
progress = Math.max(0, Math.min(mMaxProgress, progress));//先跟最大比 如果>100 就取100,再跟0比,最小到0 也就是0~100之間

if (progress != mProgress) //跟目前狀態(mProgress)不同的話
{
mProgress = progress;
invalidate();//重新繪製View
Log.d("ProgressCircle Update Progress,progress = ",Integer.toString(progress));
Log.d("ProgressCircle Update Progress,mProgress = ",Integer.toString(mProgress));
return true;
}

return false;
}
}


原始資料顏色部份是對調的,但我因為讓電池100%時看起來是綠色的,0%時為紅色的,所以就把

數據對調了一下。

值得特別注意的是  progress = Math.max(0, Math.min(mMaxProgress, progress));
                                  //先跟最大比 如果>100 就取100,再跟0比,最小到0 也就是0~100之間

這邊如果要調整最大最小值,可以透過這一行+mMaxProgress來調整。

然後invalidate();則是強制重新繪製view。

最後一塊則是Thread的部分,但thread的部分之前有寫過了,所以就不多加說明了。

其他的部分能研究的都在程式碼裡面有寫註解了,祝大家都可以順利寫出來!

附上載點



2014年9月1日 星期一

[Andorid] Service 範例-背景執行/無UI

要做一個Service要先了解,service無法自己啟動,必須要靠startService()才能啟動。

同樣的啟動後也必須要使用stopService()關閉。

在這邊需要注意的有幾點

1) startService()

2) stopService()

3) AndroidManiFest.xml增加service權限


第一點為開啟service必要條件,當然你要new一個intent

所以就是:

Intent intent = new Intent(MainActivity.this,service_class.class);
startService(intent);

MainActivity為你目前的class,service_class則是另外開的java檔(那邊則是寫service做的事情)


第二點跟第一點很類似,只有差在start / stop的差別

Intent intent = new Intent(MainActivity.this,service_class.class);
stopService(intent);

第三點就是你需要在Android ManiFest.xml裡面新增權限

否則跑起來會有

"

Unable to start service Intent: not found

"
這樣的錯誤訊息出現

好了,總結上面 MainActivity該如何寫?

package com.example.service_nonui_demo;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBar;
import android.support.v4.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.os.Build;

public class MainActivity extends ActionBarActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(MainActivity.this,service_class.class);
startService(intent);
}
@Override
public void onPause(){
super.onPause();
Intent intent = new Intent(MainActivity.this,service_class.class);
stopService(intent);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}

/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {

public PlaceholderFragment() {
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,
false);
return rootView;
}
}

}

Service部分

package com.example.service_nonui_demo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;

public class service_class extends Service{

@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
}
 

@Override
public void onStart(Intent intent, int startId) {
Toast.makeText(this, "Service start", Toast.LENGTH_SHORT).show();
}
public void onDestroy(){
super.onDestroy();
Toast.makeText(this, "Service stop", Toast.LENGTH_SHORT).show();
}
}

(記得要extends Service!)

Android ManiFest.xml部分

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.service_nonui_demo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.service_nonui_demo.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
            <service android:name="service_class"></service>
    </application>

</manifest>

記得要新增紅色字的這一行

如此一來,service就可以啟動了!

service在onStart()的地方開始運作,所以如果要做任何動作,可以寫在裡面喔!

範例只是做一個toast(跳出訊息)而已。

附上範例檔案 **點這裡下載**

End

2014年7月15日 星期二

[Android] Eclipse好用快捷鍵

在使用Eclipse時候常常很苦惱,因為不是很好用(沒背快捷鍵)

這邊記錄一下,好用的

1.Ctrl +D  = 整行刪除

2.圈起來 + Ctrl+Shift+f  = 該區域自動排版

3.圈起來 + Ctrl+/ = 該區域註解掉

4.Ctrl+Shift+O = 自動import所需的類別

大概就這些,背起來會很有用喔!