2014年9月24日 星期三

[Android] ViewPager 遇到在不同xml , 呼叫非setContentView內的R.id.物件 之解決方式

最近在改良Android的APP,先是view flipper,然後現在進階到view pager。

ViewPager可以作出很棒的特效,問題就是在xml方面,如果你要設定物件的話就必須要先

findViewbyId(),這乍聽之下沒甚麼問題,可是假如不是在main.xml檔(主要的xml)的物件呢?

我們使用findViewById()還是可以找到R.id.物件名稱,但只要你一使用的話就會出現錯誤-

java.lang.IllegalState...... NullPointerException....等等。為什麼會出現這樣的錯誤呢?

答案是因為你呼叫的id不在現在的頁面上(Android抓不到他)因此就出現nullpointerException!

在寫的時候也遇到這樣的問題,因此就上網查查看有沒有人也遇到這樣的問題?

發現這篇: 這裡

大意就是說

1.先宣告一個全域變數: private View currentViewPage

2.在 public class MyViewPagerAdapter extends PagerAdapter {} 裡面加入

  @Override
public void setPrimaryItem(ViewGroup container, int position,
Object object) {
currentViewPage = (View) object;
}

也就是把現在的position的object轉型成view然後放入currentViewPage(第一步驟設定的變數)裡

面。

3.最後   IP_edt = (EditText) currentViewPage.findViewById(R.id.IP_edt01);
      IP_edt.setText("123");

就可以用了喔!(當然IP_edt已經在前面有宣告過了)


希望可以幫助也同樣遇到困難的人!






2014年9月19日 星期五

[Android] 疑難雜症-import 外部 project有錯誤

解決import android.support.v4.app.Fragment / import android.support.v7.app.ActionBarActivity






如圖所示,加入即可。

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月3日 星期三

[Android] " no resorce found that matches the given name '' 解決方法


  1. 先使用SDK manager download 
  2. Extras -> Android Support Library
  3. 接著在File->import-> android-sdk\extras\android\support\v7(也就是剛剛下載的) -> 選appcompat
  4. 在目標的project上面點選右鍵->Properties->左邊有一欄->選Android->右下方Library->add->加入appcompat
  5. 完成。
簡單記錄一下

2014年9月2日 星期二

[Android] 如何開啟後隱藏Activty UI ?

在寫App的過程當中遇到兩個問題

1. 開機時自動啟動
2. 開啟後隱藏UI (用service在背景執行)

第一個問題可能之後幾篇會講到,不過大概就是android 會廣播一則開機訊息,你只要去抓那個訊

息就知道開機了,然後利用intent去啟動class...

第二個問題網路上比較少講到,這邊也沒有做到很完全


大概歸納為三個解決方案:

1.修改Theme 改為Theme.No_Display,但這經過實測,不行。

2.使用finish()函式,經過實測,可行。

使用方法:  在onCreate()裡面都做完事情以後新增一行 finish();

3.使用 moveTaskToBack()

public boolean moveTaskToBack (boolean nonRoot)

Added in API level 1
Move the task containing this activity to the back of the activity stack. The activity's order within the task is unchanged.
Parameters
nonRootIf false then this only works if the activity is the root of a task; if true it will work for any activity in a task.
Returns
  • If the task was moved (or it was already at the back) true is returned, else false.

Google API上面的形容,會回傳值true就是已經移到背景,false則否。

有人會使用moveTaskToBack(true),也有人說也可以改成false,但經過實際測試,兩者皆可。

但API上寫是說true是無論如何都會work,false只有activity在task的root的時候才會作用 。


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