over 1 year ago

New Update

目前更新可以讓ViewPager 與 ScrollView三者結合

#TouchHandler

#ComputeScroll

長條圖

摺線圖

圓餅圖

MyChartLib

如何使用

我盡量讓使用方法可以淺顯易懂,大大請觀看

Ex. ColumnChartView

Xml

<!-- 這邊長寬高可以自行設定-->
<com.kuo.mychartlib.view.ColumnChartView
        android:id="@+id/columnChartView"
        android:layout_width="match_parent" 
        android:layout_height="match_parent"/>

Java

private ArrayList<ColumnData> computeColumnData(int size, int maxValue) {

        int[] colors = {ChartRendererUntil.CHART_GREEN, ChartRendererUntil.CHART_PINK,
        ChartRendererUntil.CHART_RED, ChartRendererUntil.CHART_YELLOW, ChartRendererUntil.CHART_BROWN,
        ChartRendererUntil.CHART_ORANGE, ChartRendererUntil.CHART_GREY,
        ChartRendererUntil.CHART_PURPLE};

        ArrayList<ColumnData> test = new ArrayList<>();

        Random random = new Random();

        for(int i = 0 ; i < size ; i++) {
            test.add(new ColumnData("Axis X",
            random.nextInt(maxValue), colors[random.nextInt(colors.length)]));
        }

        return test;
    }

columnChartView.setColumnData(computeColumnData(size, maxValue));

通常這樣就可以了,但假設你要更新資料

columnChartView.setColumnData(computeColumnData(size, maxValue));

//updateChart() 方法為更新用

columnChartView.upadteChart(); 

目前我覺得方法這邊還可以在改一下,updateChart()這邊有點難以與一般的Lib雷同,
起因是因為每次更改都得查看是否有更改View大小,防止錯誤。

這邊待我想想。 ~

 
5 days ago

SlickGrid套件在Bootstrap3所遇到的問題

SlickGrid在Jquery套件表現非常搶眼,但也似乎是因為年久失修,造成與Bootstrap3的css衝突問題,這部分的解決方式在這裡可以看到Conflict with bootstrap 3 #742,那麼這上面有許多解法,這邊我個人是推薦下面方式。

修改CSS

[class^="slickgrid_"],
[class^="slickgrid_"] div {
  -webkit-box-sizing: content-box;
     -moz-box-sizing: content-box;
          box-sizing: content-box;
}

修改slick.grid.js

如果你的Grid元件想要讓使用者可resize那麼修改applyColumnHeaderWidths方法是一定要的,否則在拖拉的時候,又會不對齊了,但這方法只適用jQuery1.8以後的版本。

if (h.outerWidth() !== columns[i].width - headerColumnWidthDiff) {
    h.outerWidth(columns[i].width - headerColumnWidthDiff);
}
 
17 days ago

What is AOP ?

Introduction

Spring Aspect-Oriented Programming簡稱為AOP,這個模組提供了另外一種思考模式,經由實現crosscutting concerns組成Aspect,並且來達到類別與對象之間的事務管理,如日誌系統等等。

正常我們沒有使用Aspect的情況下,會將crosscutting concerns埋入商業邏輯中,這樣其實當有修改這個邏輯需求時,是有可能需要移除或者新增與修改crosscutting concerns的動作,若今天使用Spring AOP模組,我們可以將crosscutting concern完全拉出來進行撰寫,儘管後續修改商業邏輯,也不太需要理會crosscutting concern的物件。

AOP concepts(概念)

在Spring官方說明,這些AOP的術語並不是他們所進行訂製的,但也是非常的難以理解,下面將簡單介紹。

Aspect

Aspect最大的作用即是收集各商務物件中的crosscutting concerns。在Spring中除了使用XML設定方式,也建議可以使用Annotation方式進行開發,會較於簡單。

Join point

Aspect在應用程式執行時加入商務流程的點或時機稱之為Joinpoint,被呼叫的時機有可能在某個方法的前面(@before)或後面(@after),然而Joinpoint還可以讓我們取到傳入該流程的參數以及結果,也能夠攔截例外處理。

Advice

顧名思義,Advice將會處理有使用“around”、“before”和“after”等不同類型的通知,這部分後面看程式碼會較於清楚。

Pointcut

這部分可以看成,對於哪一個class的某個method進行關聯的動作,讓Advice可以知道我們要從哪個method進行通知處理。

Introduction

這不要翻譯成介紹,稱之為引入較為適當,在Java很不幸的我們無法動態的修改已編譯過的類別,但是Introduction卻可以讓你動態為這個以編譯過的類別進行新增功能等動作,確實是滿驚人的,不過我沒有試過,有興趣的大大歡迎試試看。

Target object

一個Advice被應用的對象或目標物件,如果直接使用Target Object進行撰寫橫切點等程式碼,會發現使用這種方式進行的crosscutting concerns都是需要自己設定ProxyBean,這實在是非常麻煩,但若使用Aspect則可以達到自動代理的機制。

AOP proxy

AOP框架創建的對象,用來實現Aspect。然而Spring AOP是使用動態代理的方式進行。然後代理的理解可以看看這個從代理機制初探 AOP,這邊有個簡單的小程式讓大快速理解代理機制。

Weaving

Advice被應用到物件上的過程稱為Weaving,在AOP中縫合的方式有幾個時間點:編譯時期(Compile time)、類別載入時期(Classload time)、執行時期(Runtime)。(取自於:AOP 觀念與術語)

Advice 通知的類型

@Before advice

在某連接點之前執行的通知,但不能令Join point停止執行,除非發生異常。

@After return advice

在某個接點執行成功,並且返回值得時候觸發。

@After throwing advice

在方法拋出異常退出時執行的通知。

@After(final) advice

某個接點退出的時候執行,不管接點是否異常。

@Around advice

這個功能算是滿常用的,上述的通知類型都會執行。

實戰

剛剛上面簡單的介紹了AOP,那麼現在我們這邊將要進行實作了,讓大家包括我可以更了解AOP的使用方法與情境。

目錄架構

Gradle

這邊會看到我們加入了aspects模組。

apply plugin: 'java'
apply plugin: 'eclipse'

sourceCompatibility = 1.8
version = '1.0'
jar {
    manifest {
        attributes 'Implementation-Title': 'Gradle Quickstart',
                   'Implementation-Version': version
    }
}

repositories {
    mavenCentral()
}

dependencies {
    //Spring

    def springVersion = '4.1.6.RELEASE'
    compile group: 'org.springframework', name: 'spring-context', version: "${springVersion}"
    compile group: 'org.springframework', name: 'spring-aspects', version: "${springVersion}"
    compile group: 'org.springframework', name: 'spring-test', version: "${springVersion}"
    
    //Log4j 2

    def log4jVersion = '2.8.2'
    compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: "${log4jVersion}"
    compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: "${log4jVersion}"
    compile group: 'org.apache.logging.log4j', name: 'log4j-jcl', version: "${log4jVersion}"

    //common

    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'

    //test

    testCompile group: 'junit', name: 'junit', version: '4.+'
}

test {
    systemProperties 'property': 'value'
}

uploadArchives {
    repositories {
       flatDir {
           dirs 'repos'
       }
    }
}

ContextConfig.java

這個類別就是這麼簡單,這部分其實就是取代了傳統xml的部分,今天其實也不用特地創個類別寫,但是為了整體的程式碼乾淨以及後續的擴充,通常我會特別拉出來創建,這邊做的事情有幾件事情需要了解。

  1. @Configuration 代表這是一個SpringConfig,若使用ApplicationContext讀取該類別,將會類似我們上一章的xml所設定的事情。
  2. @ComponentScan 這部分就可以指定我們所要Scan的Package,這邊後面會稍微在講解。
  3. @EnableAspectJAutoProxy 開啟讓Aspect自動代理。
@Configuration
@ComponentScan("com.spring.example.*")
@EnableAspectJAutoProxy
public class ContextConfig {

}

User.java

public class User {
    
    private String userName;
    
    private String nickName;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    @Override
    public String toString() {
        return "User [userName=" + userName + ", nickName=" + nickName + "]";
    }
    
}

UserDao.java

剛剛上面有ComponentScan的注入,這邊大家可以看到我在類別上面使用了@Component,這部分其實就是跟我們在xml使用所創建出來的bean是一樣的。

@Component
public class UserDao {
    
    public User getUserById(int id) {
        System.out.println("Method getUserById() called");
        return new User();
    }

    public int setUser(User user) {
        System.out.println("Method setUser() called");
        return 0;
    }

    public int delUserById(int id) {
        return 0;
    }
    
}

UserAspect.java

今天的主角出現了,文章開頭就介紹許多關於AOP的術語與觀念,這邊有看到@Before、@After,這兩個就是Advice通知類型,在這個Advice後面大家可以看到execution以及指定類別、方法,這邊就是Pointcut。

@Aspect
@Component
public class UserAspect {

    @Before("execution(* com.spring.example.dao.UserDao.getUserById(..))")
    public void logBeforeV1(JoinPoint joinPoint) {
        System.out.println("UserAspect.logBeforeV1() : " + joinPoint.getSignature().getName());
    }

    @Before("execution(* com.spring.example.dao.UserDao.*(..))")
    public void logBeforeV2(JoinPoint joinPoint) {
        System.out.println("UserAspect.logBeforeV2() : " + joinPoint.getSignature().getName());
    }

    @After("execution(* com.spring.example.dao.UserDao.getUserById(..))")
    public void logAfterV1(JoinPoint joinPoint) {
        System.out.println("UserAspect.logAfterV1() : " + joinPoint.getSignature().getName());
    }

    @After("execution(* com.spring.example.dao.UserDao.*(..))")
    public void logAfterV2(JoinPoint joinPoint) {
        System.out.println("UserAspect.logAfterV2() : " + joinPoint.getSignature().getName());
    }

}

AopTest

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {ContextConfig.class})
public class AopTest extends AbstractJUnit4SpringContextTests{

    @Resource
    private UserDao userDao;
    
    @Test
    public void aopTest() {
        
        userDao.getUserById(1);
        userDao.setUser(new User());
        
    }
    
}

Result

當大家執行AopTest時,就可以在Console看到這幾行。

UserAspect.logBeforeV1() : getUserById
UserAspect.logBeforeV2() : getUserById
Method getUserById() called
UserAspect.logAfterV1() : getUserById
UserAspect.logAfterV2() : getUserById
UserAspect.logBeforeV2() : setUser
Method setUser() called
UserAspect.logAfterV2() : setUser

結語

AOP是一個強大的模組,使用它可以讓crosscutting concerns動作解耦,當產品上線後,維運就是必要的,若能夠讓這些動作切開來,令商業邏輯的流程乾淨,是非常棒的一件事情,再加上使用Annotation方式進行設定,大家應該會發現比傳統的xml設定清楚、乾淨吧!

希望這邊簡單的文章對大家有所幫助!!

Reference

AOP 觀念與術語
Aspect Oriented Programming with Spring
Spring 实践:AOP

 
18 days ago

What is Spring ?

Spring是一個lightweight(輕量級)的framework(框架),它可以想像是各種框架的集成,因為它可以支援許多種框架,例如Struts, Hibernate, Tapestry, EJB, JSF等等,它包含幾種模組,例如IOC, AOP, DAO, Context, ORM, WEB MVC等等,不過我們第一步需要先了解IOC(控制反轉)以及Dependency Injection(依賴注入)。

What is IOC、Dependency Injection ?

Spring容器實現了IOC,也是這整個框架的核心所在,容器將會創建、管理物件直到銷毀,並使用Dependency Injection(依賴注入)來管理這些物件(Bean),然而Spring提供這兩種IOC容器,分別為BeanFactory、ApplicationContext,這兩部分的差別,後將依序簡單的探討。

BeanFactory
這是Spring提供最簡單的IOC容器,是無法使用大部分模組的,例如AOP、Integration、Web等等,因此僅適合在有限資源的環境下使用,通常用於測試與非生產環境。

ApplicationContext
大神也推薦使用這個IOC容器,該容器繼承BeanFactory並提供了更多的功能,因此我們可以方便的使用各種模組,來幫助開發。

Hello Spring

上面稍微介紹了Spring的核心功能,那麼我們現在來給它實作看看,應該會更加了解Spring容器。

目錄架構

Gradle

Gradle這邊我們只要引入spring-context即可,這個lib是Spring的核心,其他引入的部分並沒有使用到,只是我直接copy其他部分的程式碼XD。

apply plugin: 'java'
apply plugin: 'eclipse'

sourceCompatibility = 1.8
version = '1.0'
jar {
    manifest {
        attributes 'Implementation-Title': 'Gradle Quickstart',
                   'Implementation-Version': version
    }
}

repositories {
    mavenCentral()
}

dependencies {
    //Spring

    def springVersion = '4.1.6.RELEASE'
    compile group: 'org.springframework', name: 'spring-context', version: "${springVersion}"
    
    //Log4j 2

    def log4jVersion = '2.8.2'
    compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: "${log4jVersion}"
    compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: "${log4jVersion}"
    compile group: 'org.apache.logging.log4j', name: 'log4j-jcl', version: "${log4jVersion}"

    //common

    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'

    //test

    testCompile group: 'junit', name: 'junit', version: '4.+'
}

test {
    systemProperties 'property': 'value'
}

uploadArchives {
    repositories {
       flatDir {
           dirs 'repos'
       }
    }
}

beans.xml

Spring最經典的就是使用xml設定了,那麼其實有另外一種JavaConfig設定方式,我還在考慮要不要放XDD

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" 
  "http://www.springframework.org/dtd/spring-beans.dtd"> 
<beans> 
    <bean id="helloBean" class="com.spring.example.HelloSpringBean"> 
        <property name="message">
            <value>Hello Spring!!</value>
        </property> 
    </bean> 
</beans>

HelloSpringBean.java

這邊大家可以對應到上面的XML設定,會發現我們設定了class位置,並命名id為helloBean以及設定message為Hello Spring!!,然而這邊提醒一下,大家還是要習慣加入toString,會比較方便測試。

public class HelloSpringBean {

    private String message;
    
    public void setMessage(String message) {
        this.message = message;
    }
    
    public String getMessage() {
        return message;
    }

    @Override
    public String toString() {
        return "HelloSpringBean [message=" + message + "]";
    }
    
}

HelloSpringApp.java

這裡就是整個Spring容器啟動的時候,我們可以看到我使用了ClassPathXmlApplicationContext的ApplicationContext類別來加入上述所設的beans.xml,並且取出id為helloBean的物件。

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class HelloSpringApp {

    private static Logger logger = LogManager.getLogger(HelloSpringApp.class);

    @SuppressWarnings("resource")
    public static void main(String[] args) {
        
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        
        HelloSpringBean helloSpringBean = (HelloSpringBean) context.getBean("helloBean");
        
        logger.info(helloSpringBean);
    }
  
}

Result Console

大家可以看看最後一行,是不是出現我們剛剛設定的message了呢?

22:56:15.943 [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@43bd930a: startup date [Tue Sep 05 22:56:15 CST 2017]; root of context hierarchy
22:56:15.973 [main] INFO  org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [beans.xml]
22:56:16.049 [main] INFO  com.spring.example.HelloSpringApp - HelloSpringBean [message=Hello Spring!!]

結語

Spring整個核心圍繞著IOC、DI使用,不論加上哪一種模組,我們都需要設定(Xml or JavaConfig),很多人雖然使用著Spring開發,但卻IOC、DI非常的陌生,更不用說BeanFactory的生命週期了,了解基礎的東西才能讓我們更快速地排除問題,今天這篇文章非常的簡單,我甚至還沒有深入到BeanFactory整個產生Bean的過程,這部分就牽扯到底層的部分。

寫這系列文也是希望自己不要只會開發,也要了解底層的做法,藉此鞭策自己持之以恆的學習,軟體真的是個坑...學起來放(老屁股之路? XD)。

Reference

Spring Tutorial
開源框架: Spring Gossip
极客学院 - IoC 容器

 
about 1 year ago

Question

Given two binary trees, write a function to check if they are equal or not.

Two binary trees are considered equal if they are structurally identical and the nodes have the same value.

這到題目要我們判斷兩個Tree是否相同。

Resolve

這題的解法我們需要依序去判斷兩個tree是否為空,優先判斷若是空值就先回傳true or false,後者在判斷val值是否正確,因此這提示不困難的,但鄙人目前只會土法煉鋼,程式碼大概如下。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        
        if(p == null && q == null) {
            return true;
        }
        
        if(p == null && q != null || p != null && q == null) {
            return false;
        }
        
        if(p.val != q.val) return false;
        
        boolean res = isSameTree(p.left, q.left);
        
        if(!res) return false;
        
        res = isSameTree(p.right, q.right);
        
        if(!res) return false;
        
        return true;
       
    }
}
 
about 1 year ago

醜陋的數字呀! 數字中有質因數2 3 5便判斷為醜陋的數,假設今天並沒有全部符合2 3 5代表並非醜陋數

而1,我們通常都會判定為醜陋的數。

public class Solution {
    public boolean isUgly(int num) {
        while (num >= 2) {
            if (num % 2 == 0) num /= 2;
            else if (num % 3 == 0) num /= 3;
            else if (num % 5 == 0) num /= 5;
            else return false;
        }
        return num == 1;
    }
}

程式碼解說:

通常我們1都醜陋數,因此我們從2開始算,每次運算我們都會讓num去取2 3 5的餘數,假設為0變取代目前的num,

這算是一種簡單的作法,如果使用for迴圈去算可能會需要很多時間,因此這樣的作法是比較正確的。

假設我們取到一個不屬於2 3 5的質因數,那麼我們當然就是口愛的數字惹,也將不必再運算,直接給他傳回去。

 
about 1 year ago

兩數相加不使用+/-來達成,其實很簡單的,直接位元運算,但我們常常忘記位元運算這種東西,包括我也沒有做過這樣的運算。

這個程式碼是我目前覺得最好的答案,簡單又清楚,速度上也可以接受。

以下可以簡單解釋一下程式碼

public class Solution {
    public int getSum(int a, int b) {
        while(b!=0) {
            int c = a & b; 
            a = a ^ b; 
            b = c << 1; 
        }
        return a;
    }
}

其實就是很純粹的位元運算,下面用簡單的方式圖解一下。

假設a = 5, b = 2, answer = 7;

a = 0001 (1)
b = 0010 (2)

c a b
0101 AND 0101 XOR 0000 向左位移 當b=0回傳a=0111(7)
0010 0010 ----
---- =>> ---- 0000
0000 0111

這是很簡單的解法,利用位移讓我們達到相加的道理,然後計算基本上就是使用0與1,其實這才是真正的加法!

 
about 1 year ago

肥宅最近工作繁忙,也已經很久沒有寫部落格了,應該會開始慢慢回復以前的情形,也許1~2禮拜一篇文章之類的。
工作上的需求常常會使用到MySQL,然而在SQL上,想要正規化資料庫,那關聯肯定是其中最不可或缺的因素之一。
這邊分享一下關聯的簡單用法,主要是使用InnerJoin其他種Join目前還沒有榮幸去研究。

假設我們這邊有三個表格依序為table1, table2, table3;

id country
1 台灣
2 美國
3 日本
id c_id city
1 1 高雄
2 1 台中
3 3 東京
id c_id food
1 1 豬血糕
2 2 麵線
3 3 黑輪

我通常都會創建一個檢視表,放便以後讀取,不需要每次都寫SELECT。

CREATE VIEW food_posts AS SELECT table3.id, table3.food, table2.city, table1.country
FROM table3
INNER JOIN table2 ON table3.c_id = table2.id
INNER JOIN table3 ON table2.c_id = table1.id
WHERE table1.city = '台灣';

這樣就可以創見一個所有台灣美食的檢視表,但是要稍微注意一下Join的順序,使用正規化方式所開發的資料庫會較於維護,但要注意一點,若要將上層刪除掉,下層相關的也要一併刪除,否則怕以後擷取資料會有錯誤。

 
about 1 year ago

其實CodeIgniter是有使用SQL ORM的,他的ORM框架是ActiveRecord,也是一款有名的ORM。

使用ORM的好處就不多說了,利大於弊,好用就好orz...

這篇也算是個筆記,將會記錄CodeIgniter如何擷取資料與新增等動作,章節應該是不多,

畢竟常用的也就那幾項動作,頂多個加密之類的,但加密其實可以直接寫在SQL那邊。

Models

<?php
class NameModel extends CI_Model {
  public function getAllName() {
    $query = $this->db->get('test');
    //$query = $this->db->get_where('test', array('id' => '1'));
    
    foreach ($query->result() as $row) {
      echo $row->id;
      echo $row->name;
    }
    return $query->result();
  }
}
?>

上面可以看到我們第一行query是取得test資料庫全部的資料,
下一行則是使用where取得我們所想要的資料,然而getWhere已經不適用了,要注意目前許多方法有改。

Controllers

<?php
class Name extends CI_Controller {
  public function index() {
  
    $this->load->model('NameModel', '', TRUE);
    $data = $this->NameModel->getAllName();
    
    foreach ($data as $row) {
      $name['name'] = $row->name;
      $this->load->view('test', $name);
    }
  }
}
?>

這邊很簡單的,加入我們所撰寫好的Model,並且打開資料庫以及取得資料。
回傳的時候會使用$name['name']這種命名方式,為了讓view取得變數。

Views

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>CI教學</title>
  </head>
  <body>
    <?= $name?></br>
  </body>
</html>

我們這邊可以使用<?= $name?>語法,對應剛剛我們所定義的變數['name']
就可以取得資料了。

對於CodeIgniter還不是很了解,所以這次純粹是個筆記,我認為學新東西都得由深入淺,
這也是朋友點醒我的,原先我每次研究東西都是從基礎架構開始研究,但這樣其實會花費太多時間。
所以先將整個框架使用順手後,再去了解其原理,我想也會比較快理解。

 
over 1 year ago

最近肥宅朋友揪我一起寫個Googel Chrome Plugin,剛好會使用到Facebook API,

但這些API都需要AccessToken的幫忙才能夠取得資料,然而通常都會有時效性,那要如何取得永久的呢?

去挖了一下靠北工程師的製作過程,裡面剛好解決了永久性的AccessToken,參考這裡

看一下第三步驟如何取得永久性的AccessToken,不過到底是不是真的可以永久,待肥宅試試看。